2

I have a list of checkboxes, I'm able to update their array of object when user check or uncheck. But I have one problem, the other value remain unchange even when i update the state.

http://jsbin.com/qomekohile/1/edit?js,console,output

class HelloWorldComponent extends React.Component {

  constructor(){
    super()
    this.handleChange = this.handleChange.bind(this);
    this.state = {
      "fruits":[
        {"name":"banana","value":true},
        {"name":"watermelon","value":false},
        {"name":"lemon","value":true},
        {"name":"other","value":true,"other_value":"Durian"}
      ]
    }
  }

  handleChange(e,key){
    let nxState = Object.assign({}, this.state)
    nxState.fruits.find(obj => {



      if (obj.name === key) {
        obj.value = e.target.checked

        if(obj.name === 'other' && obj.value === false){
           obj.other_value = ''; //why this won't work
        }

        return true
      } else {
        return false
      }
    });
    this.setState(nxState)
  }

  render() {
    return (
      <div>
        {this.state.fruits.map(obj => 
           (obj.name === 'other') ?

           <div>
           <label>{obj.name}</label>
           <input onChange={(e) => this.handleChange(e, obj.name)} type="checkbox" defaultChecked={obj.value} />
           <input type="text" defaultValue={obj.other_value} />
           </div>
           :
           <div key={obj.name}>
           <label>{obj.name}</label>
           <input onChange={(e) => this.handleChange(e, obj.name)} type="checkbox" defaultChecked={obj.value} />
            </div>
         )}
         <br />
         <pre>{JSON.stringify(this.state.fruits,null,2)}</pre>
       </div>
    );
  }
}
Mellisa
  • 605
  • 3
  • 12
  • 22

1 Answers1

0

You should set value and define some changeHandler to make this input controlled (defaultValue attr is only for uncontrolled fields):

<input type="text" value={obj.other_value} onChange={(e) => this.handleOtherChange(e)}/>

//example `other` text input change handler
handleOtherChange(e){
    let nxState = {...this.state, fruits: [
       ...this.state.fruits,
    ]};
    nxState.fruits.find(obj => obj.name === 'other').other_value = e.target.value;
    this.setState(nxState)
}

Also please note that Object.assign performs only shallow merge of objects and since mutating state object directly should be avoided you have to perform deep state copy (to copy also nested objects and arrays) which can be done using spread syntax as in above example but generally I'd recommend using immutability-helper or Immutable to perform state updates immutable way.

Bartek Fryzowicz
  • 6,464
  • 18
  • 27
  • I got this warning `Warning: ...contains an input of type text with both value and defaultValue props` – Mellisa Mar 08 '17 at 11:13
  • ah this is more elegant, great! (although I have no idea why the problem exist actually.) so the problem is cause by Object.assign? – Mellisa Mar 08 '17 at 11:33
  • Great :) In your case problem occured because 'other_value' text input was uncontrolled - in a nutshell it means it wasn't aware of component state and didn't reflect state value so even if you updated 'other_value' in state this input didn't know about this. You can read more about uncontrolled components here: https://facebook.github.io/react/docs/uncontrolled-components.html. – Bartek Fryzowicz Mar 08 '17 at 11:43
  • In your case using Object.assign didn't cause main problem but could cause other problems in future because in React you should never mutate state directly - and if you mutate nested state objects which wasn't copied you actually muatate state directly. You can read more here: http://stackoverflow.com/questions/35867038/what-the-difference-of-this-state-and-this-setstate-in-reactjs – Bartek Fryzowicz Mar 08 '17 at 11:43