Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 632
user517153 Avatar asked Dec 23 '15 07:12

user517153


People also ask

How can we prevent default behavior in React?

We can prevent this default behaviour by making a small modification to the definition of the handleSubmit function. We call a preventDefault on the event when submitting the form, and this will cancel the default event behavior (browser refresh) while allowing us to execute any code we write inside handleSubmit.

Which event can be used to stop event propagation in React?

stopPropagation() within a ReactJS component to stop a click event from bubbling up and triggering a click event that was attached with JQuery in legacy code, but it seems like React's stopPropagation() only stops propagation to events also attached in React, and JQuery's stopPropagation() doesn't stop propagation to ...

Why does React use synthetic events?

Benefits of using synthetic events: Cross browsers applications are easy to implement. Synthetic events are that ReactJS reuses these events objects, by pooling them, which increase the performance.


1 Answers

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.

like image 111
Matthew Herbst Avatar answered Sep 20 '22 15:09

Matthew Herbst