Jay Cee
Jay Cee

Reputation: 1965

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!

Upvotes: 0

Views: 180

Answers (2)

wintvelt
wintvelt

Reputation: 14101

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>
  }
}

Upvotes: 1

Yuya
Yuya

Reputation: 2311

Slightly easier way to maintain is to store the exact field variable name in the name instead, and do:

handleChecked(e){
  this.setState({[e.value.name]: true});
  ...
}

Upvotes: 0

Related Questions