1

So say you have a child component that returns a number of input fields and takes a data object as a prop.

class InputFields extends React.Component {

  render() {

    let data = this.props.data

    return (
      <div className='input-group'>
      <input type='text' defaultValue={data.name} onChange={this.props.update} />
      <input type='text' defaultValue={data.age} onChange={this.props.update} />
      <input type='text' defaultValue={data.email} onChange={this.props.update}/>
      </div>
    )
  }
}

And the parent component might look something like:

class App extends React.Component {

 constructor(props) {
   super(props)
   this.state = {
    items: getItemsFromSomewhere()
  }
 }

 update(event) {
   /* Somehow update main state here? */
 }

 render() {
   /* imagine this as an array of objects, each with name, age, email ,etc. */
   var items = this.state.items.map(item => {
    return <InputFields data={item} update={this.update.bind(this)} />
  })
  return <div className='itemLists'> {items} </div> 
 } 

}

Within this.props.update I want to set the corresponding state for the data object which was passed down. But the onChange function only receives the synthetic event which only provides a reference to the DOM element itself.

Whats the best way to find which state field needs to be updated, e.g., when the input field for data.name is changed, how can I propagate that up to the state within the main parent?

I know that refs could be used, but how can I know which ref to grab on the change event? Do I just have to iterate through all of them?

Aweary
  • 2,302
  • 17
  • 26
  • Couldn't you just let your update function take another argument that specifies what called it? Then you can modify each of your onClicks to pass something different for that second argument – Michael Parker Aug 10 '15 at 16:05
  • possible duplicate of [Pass props to parent component in React.js](http://stackoverflow.com/questions/22639534/pass-props-to-parent-component-in-react-js) – Aweary Aug 10 '15 at 16:06
  • Not sure about that. It doesn't look like you're trying to send the child's props back to the parent, but rather some information regarding user input, which was gathered by the child and needs to be sent to the parent. Unless I am misreading your question – Michael Parker Aug 10 '15 at 16:10
  • Can you post an example of how what you're suggesting might be different? Seems like you're suggesting the same thing, i.e., just adding additional arguments when binding the function – Aweary Aug 10 '15 at 16:16
  • I guess I didn't read that link you posted very carefully - that's exactly what I'm suggesting. – Michael Parker Aug 10 '15 at 16:36
  • So my question is, I know I can bind it to the `item` or even the `index` when mapping it, but how can I get a reference to which input field is being changed? For example, if the input field for `data.name` is changed, binding it to `item` will just return the entire `item` object with `name`, `age`, and `email`. How can I get a reference to which input field is being updated so I can update that specific property? Would I have to just bind the event on the input field itself and pass an identifier from there? – Aweary Aug 10 '15 at 16:40
  • Honestly, I'm not sure about the best way to accomplish this. A potential solution I can think of is assigning each input a ref, then passing that as one of the arguments. – Michael Parker Aug 10 '15 at 17:45

2 Answers2

1

You could do it like this:

class InputFields extends React.Component {

  _onChange(field) {
    this.props.update(field, this.props.data);
  }

  render() {

    let data = this.props.data

    return (
      <div className='input-group'>
      <input type='text' defaultValue={data.name} onChange={this._onChange.bind(this, 'name')} />
      <input type='text' defaultValue={data.age} onChange={this._onChange.bind(this, 'age')} />
      <input type='text' defaultValue={data.email} onChange={this._onChange.bind(this, 'email')}/>
      </div>
    )
  }
}

and then in the update function of the parent component you get the changed field as a string plus the whole data object:

update(field, item) {
    // do stuff
    var value = item[field];
}
Simon
  • 128
  • 1
  • 7
  • This is actually what I ended up doing, so I'll make this as correct! – Aweary Aug 11 '15 at 19:21
  • Nice, thanks! Your last comment in your answer already sounded like you figured this out, but I only saw that comment after I posted my answer (and Initially I posted a wrong answer to your problem). Cheers! – Simon Aug 11 '15 at 19:32
  • This will work but you're using props as state this not advised – François Richard Aug 13 '15 at 21:37
  • state is stored in the parent and passed to children via props. The actual state is still within the parent. – Aweary Aug 14 '15 at 19:19
0

edit: You were right I answered something dumb too quickly. Below is a brand new answer that'll fit your needs I think.

Why not using event.target.value ? and you just use an event handler ?

In the child:

<input type='text' id="name" defaultValue={data.name} onChange={this.props.update("name")} />`

And the update function:

update(stateToChange) {
   var self = this;
   return function(e) {
       var newState = {};
       newState[stateToChange] = e.target.value;
       self.setState(newState);
   }
}

Hope it helped this time :)

François Richard
  • 6,817
  • 10
  • 43
  • 78
  • If I add an event listener directly to the DOM then there's no way for me to get information regarding the React component, which would be useless to me. The post you linked recommends passing a bound function to the children with additional arguments. The function comes from the parent, which is how React data flow should work. – Aweary Aug 10 '15 at 19:07