user517153 user517153 - 4 years ago 125
Javascript Question

React event hierarchy issue

What is the best way to handle state changes in a deep node which also need to be handled by a parent node. Here is my situation:



<Table>
<Row prop={user1}>
<Column prop={user1_col1} />
<Column prop={user1_col2} />
</Row>
<Row prop={user2}>
<Column prop={user2_col1} />
<Column prop={user2_col2} />
</Row>
<TableFooter>
<FooterColumn prop={sum1} />
<FooterColumn prop={sum2} />
</TableFooter>
</Table>





Whenever someone is changing anything in the column property I only have to maintain the state of this value within that Column component. However, I now would like a sum of these values in the FooterColumn component. What is the best way to achieve this?

If I'm going to pass up the state change I must keep states in multiple places and then pass it down, this is a lot of tedious work. Is it best to use EventEmitters or am I missing something?

Answer Source

So, all you need is to keep track of the state in the parent component, and share the state update function with the children:

var Parent = React.createClass({
  getInitialState: function() {
    return {
      users: [
        {name: 'Matt', values: [1, 2]},
        {name: 'user517153', values: [4, 5]}
      ]
    };
  },
  updateValue: function(rowId, colId, newValue) {
    var newUsersState = this.state;
    newUsersState.users[rowId].values[colId] = newValue;
    this.setState({users: newUsersState});
  },
  render: function() {
    var rows = this.state.users.map(function(user, r) {
      var cols = user.values.map(function(value, c) {
        return (
          <Column key={c} prop={value} rowId={r} colId={c} onChange={this.updateValue}/>
        );
      });

      return (
        <Row key={r} prop={user}>
          {cols}
        </Row>
      );
    });

    // Yes, it could be more efficient if you did it all in one map/forEach - doing this in a second one for clarity
    var footerCols = this.state.users.map(function(user) {
      var sum = 0;
      user.values.forEach(function(value) { sum+= value; });
      return (
        <FooterColumn prop={sum} />
      );
    });

    return (
      <Table>
        {rows}
        <TableFooter>
          {footerCols}
        </TableFooter>
      </Table>
    );
  }
});

In your Column class, you simply need something along the lines of:

var Column = React.createClass({
  onChange: function(event) {
    var props = this.props;

    var newValue = event.target.value; // Get the new value somehow - this is just an example
    props.onChange(props.rowId, props.coldId, newValue);
  },
  render: function() {
    var props = this.props;

    return (
      <td onChange={this.onChnage}>{props.prop}</td>
    );
  }
});

Hope that makes sense.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download