Jay Cee Jay Cee - 6 months ago 25
Javascript Question

Reactjs how to manage multiple interactions between component

I struggling and a bit lost with react about one thing.

I have a row with 5 columns.

Each columns have a checkbox, an input and a button.

So if you look at it in a standard way, it looks like this:

render() {
return (
<div className="sbm-sources">
<div className="col-md-2 sbm-source">
<input type="checkbox" name="c1"/>Add to source.
<br />
<input type="text" name="i1" size="10" onClick={this.expandInput} placeholder="Enter you query." />
</div>
<div className="col-md-2 sbm-source">
<input type="checkbox" name="c2"/>Add source to blipp.
<br />
<input type="text" name="i2" size="10" onClick={this.expandInput} placeholder="Enter you query." />
</button>
</div>
<div className="col-md-2 sbm-source">
<input type="checkbox" name="c3" />Add source to blipp.
<br />
<input type="text" name="i3" size="10" onClick={this.expandInput} placeholder="Enter you query." />
</button>
</div>
<div className="col-md-2 sbm-source">
<input type="checkbox" name="c4" />Add source to blipp.
<br />
<input type="text" name="i4" size="10" onClick={this.expandInput} placeholder="Enter you query." />
</div>
<div className="col-md-2 sbm-source">
<input type="checkbox" name='c5' />Add source to blipp.
<br />
<input type="text" name="i5" size="10" onClick={this.expandInput} placeholder="Enter you query." />
</div>
</div>
);
}
};


The thing is, each column can be validated separately but I need to know which one is trigger.

I know how to do it using the name of each one, but I am not sure that creating a state for EACH input / checkbox and then check which one is triggered one for then associating the data before sending a POST request is the best option here.

ex:

handleChecked(e){
if (e.value.name === c1){
this.setState({checkedC1: true});
}

...
}


This would quickly become messy and hard to maintain, or to make adaptable.

The thing is, when I want to do my Post request, I would love to receive an int. For example, if the checkbox (and / or) input filled is from the first column, the int received would be 0.

Is there ean elegant way of doing so with react? What would you suggest?
I am too much into my code and my lack of experience make me blind about it.

Many thanks!

Answer

You would need to keep the state of all your columns inside the parent component, because from there you send your post request.

  • create an array of column data, and put the array in state
  • inside render, use .map() to loop over the array and render a Column for each item in the array
  • optional: put the column inside a separate (stateless) component.

Your state could be like:

// as part of your constructor
let initialColumns = [
  { checked: false, checkText: "Add to source.", inputPh="Enter your query.", value: ""},
  ...
  { checked: false, checkText: "Add source to blipp.", inputPh="Enter your query.", value: ""}
];

this.state = { colDataArr: initialColumns }

And in your render do:

render() {
  let expandInput = this.expandInput;
  <div>
    {this.state.colDataArr.map( colItem, index => {
      <Column
        checked = {colItem.checked}
        checkText = {colItem.checkText}
        ...
        expandInput = {(e) => { expandInput(e) }}  // <== special here
        colID = {index}                            // and here
    })}
  </div>
}

Create a (stateless) <Column> component that takes the function expandInput as a prop, alongside the other variable props. Whenever this function is called, you get the event, but also the index of the column (from 0-4).

That way, inside expandInput, you can handle one individual update

expandInput(event, type, index) {
  // create copy of column object, to avoid mutating state directly
  let origColData = this.state.colDataArr[index]
  let newColData = {
    checked = origColData.checked, 
    checktext = origColData.checkText,
    ...
  }
  // now, do whatever you need with the event data to update the newColData
  if (type == "checkbox") {
    let checked = e.target.checked
  } else {
    ...
  }
  // copy the array and replace the updated one
  let newColArr = this.state.colDataArr.slice()
  newColArr[index] = newColData
  // set the new state
  this.setState({ colDataArr : newColArr })

}

UPDATE
And your shiny new stateless component could look something like this:

class Column extends React.Component {
  constructor(props) {
    super(props)
  }
  render() {
    <div className="col-md-2 sbm-source">
       <input type="checkbox" 
         onClick={(e) => this.props.expandInput(e,"checkbox", this.props.colID)}/>
       {this.props.checkText}
       <br />
       <input type="text" size="10" 
         onChange={(e) => this.props.expandInput(e,"text", this.props.colID)} 
         placeholder={this.props.inputPH} />
       <button
         onClick={(e) => this.props.expandInput(e,"button", this.props.colID)}>
         Do something
       </button>
     </div>
  }
}
Comments