React - Render Component between two existing ones

I am pretty new on using React, what i'm trying to build is a dynamic form in which user can add/remove fields, the problems come when rendering after a row (field) is added

Here is my Row Component, which I use as a template to fill by props

class Row extends React.Component {
    constructor(props){
        super(props);
        this.addLine = this.addLine.bind(this)
        this.handleTitleChange = this.handleTitleChange.bind(this)
        this.handleInquisitionChange = this.handleInquisitionChange.bind(this)
    }

state = {
    pos: this.props.pos,
    title: this.props.title,
    inquisition: this.props.inquisition
}

addLine() { 
    this.props.addLine(this.state.pos);
}

handleTitleChange = async (event) => {
    await this.setState({title: event.target.value});
    this.props.updateRowState(this.state.pos, this.state.title, "title")    
}

handleInquisitionChange = async (event) => {
    await this.setState({inquisition: event.target.value});
    this.props.updateRowState(this.state.pos, this.state.inquisition, "inquisition")    
}

render(){
  return(
    <div className="w3-row odg-line">
        <div className="w3-col m2" style={{paddingRight: "8px"}}>
            <input type="text" name="titolo[]" placeholder="Argomento" style={{width:"100%"}} onChange={this.handleTitleChange} required/>
        </div>
        <div className="w3-col m4" style={{paddingRight: "8px"}}>
            <textarea form="convocazione" name="istruttoria[]" placeholder="Istruttoria" style={{width:"100%"}} onChange={this.handleInquisitionChange} required></textarea>
        </div>
        <div className="w3-col m1">
            <button type="button" style={{padding:0, height: "24px", width: "24px"}} className="w3-red w3-button w3-hover-white" onClick={() => this.addLine()}>+</button>
        </div>
    </div>
  )
}

}

And this is its parent Convoca, as you can see by its addLine method whenever a "plus" button is pressed it pushes a row after that and updates component state, as far as I know this should cause the component to render again but when it comes it just adds the new one after the already rendered ones

class Convoca extends React.Component {
    constructor(props) {
        super(props)
        this.handleSubmit = this.handleSubmit.bind(this);
        this.addLine = this.addLine.bind(this);
    }

state = {
    rows: [
        {pos:0, title: "", inquisition:  ""},
        {pos:1, title: "", inquisition:  ""}
    ]
}

async addLine(position){
    let buffer = this.state.rows;
    buffer.splice(position+1, 0, {pos: position+1, title: "", inquisition:  ""})
    for(let i = position+2; i<buffer.length; i++){
        buffer[i].pos++;
    }
    await this.setState({rows: buffer})
}

handleChangeState = async (pos, val, field) => {
    let buffer = this.state.rows;
    if(field === "title") buffer[pos].title = (field === "title" ? val : null);
    if(field === "inquisition") buffer[pos].inquisition = (field === "inquisition" ? val : null);
    await this.setState({rows: buffer})
}

handleSubmit(){
    console.log("submitted")
}

render() {
    return(
        <div className="w3-main" style={{marginLeft:"340px", marginRight:"40px"}}>
            <form action="/convocazione" id="convocazione">  
            {   this.state.rows.map((row) => (
                <Row updateRowState={(pos, val, field) => this.handleChangeState(pos, val, field)} addLine={(pos)=>this.addLine(pos)} pos={row.pos} title={row.title} inquisition={row.inquisition}></Row>)
            ) }
            <input className="w3-red w3-button w3-hover-white" type="submit" value="Convoca"/>
            </form>
        </div>
    );
}

}

Answers:

Answer

I would implement addLine function another way

Take a look at the snippet.

const createElement = React.createElement;

class Row extends React.Component {
  render() {
    const {
      position
    } = this.props;

    return createElement('div', null, [
      createElement('input', {
        type: "text",
        value: this.props.title
      }),
      createElement('button', {
        type: "button",
        onClick: () => this.props.onAdd(position)
      }, '+')
    ]);
  }
}


class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      rows: [{
          id: 0,
          position: 0,
          title: 'id 0 position 0'
        },
        {
          id: 1,
          position: 1,
          title: 'id 1 position 1'
        },
      ]
    };

    this.addLine = this.addLine.bind(this);
  }

  addLine(position) {
    const {
      rows
    } = this.state
    const newId = rows.reduce((acc, row) => acc > row.id ? acc : row.id, 0) + 1

    position = position + 1;

    const newRows = [
      ...rows.filter(row => row.position < position),
      {
        id: newId,
        position,
        title: `id ${newId} position ${position}`
      },
      ...rows.filter(row => row.position >= position).map(row => ({ ...row,
        position: row.position + 1,
        title: `id ${row.id} position ${row.position + 1}`
      }))
    ]

    newRows.sort((prev, next) => prev.position - next.position)
    this.setState({
      rows: newRows
    })
  }

  render() {
    const items = this.state.rows.map(item =>
      createElement(Row, {
        key: item.id,
        title: item.title,
        position: item.position,
        onAdd: this.addLine
      })
    )

    return createElement('form', null, items);
  }
}

var rootElement = createElement(App, {}, )

ReactDOM.render(rootElement, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root">
</div>

Make sure every Row has key prop with unique id

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us Javascript

©2020 All rights reserved.