Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

After updating props, this.props is always === nextProps / prevProps

I've got a jsbin for this issue here: http://jsbin.com/tekuluve/1/edit

In an onClick event I'm removing an element from the model, and re-rendering the app. But strangely, in componentWillReceiveProps() (and componentWillUpdate, and componentDidUpdate too), nextProps is always === to this.props, regardless of what I do.

/** @jsx React.DOM */
var Box = React.createClass({
  render: function() {
    return (
      <div className="box" onClick={ UpdateModel }>
        { this.props.label }
      </div>
    );
  }
});

var Grid = React.createClass({
  componentWillReceiveProps: function(nextProps) {      
    // WTF is going on here???
    console.log(nextProps.boxes === this.props.boxes)        
  },
  render: function() {
    var boxes = _.map(this.props.boxes, function(d) {
      return (<Box label={ d.number } />);
    });

    return (
      <div className="grid">
        { boxes }
      </div>
    );
  }
});

var model = [
  { number: 1 },
  { number: 2 },
  { number: 3 },
  { number: 4 },
  { number: 5 }
];

function UpdateModel() {
  React.renderComponent(
    <Grid boxes={ _.pull(model, _.sample(model)) } />,
    document.body
  );
}

React.renderComponent(
  <Grid boxes={ model } />,
  document.body
);

I need nextProps to be different to this.props after it has been updated via UpdateModel(), in the componentWillReceiveProps() lifecycle event.

like image 211
user2309185 Avatar asked Jul 28 '14 06:07

user2309185


3 Answers

Something like this occurred for me using Flux Stores to store my state in the manner suggested by the official Todo List tutorial (http://facebook.github.io/flux/docs/todo-list.html). For anyone else finding this after following the tutorial, a problem seems to arise in the TodoStore in the getAll() method, because it returns a direct reference to the internal data object:

getAll: function() {
  return _todos;
}

This seems to breaks the ability of the lifecycle methods like componentDidUpdate(prevProps) to distinguish between the old and new props. I think the reason is that by passing a direct reference to the Store's data object into the views, the state/props effectively change immediately when the Store changes rather than after new values are passed down through the lifecycle methods, so old and new props always contain the same values. This could be solved by passing a copy of the internal data object _todos rather than the object itself,

for example, when _todos is an object,

getAll: function() {
  return JSON.parse(JSON.stringify(_todos));
}

If _todos is an array, return _todos.slice() can be used instead.

In general, if using a Store to contain state information and call it from your controller-views, it seems advisable to return a copy of the data rather than a reference to the original, since the original will be mutated when state changes occur.

like image 89
Adam Stone Avatar answered Oct 04 '22 22:10

Adam Stone


=== will check if it's the same object. It seems that what you're doing is mutating the value that the boxes property points to.

like image 26
August Lilleaas Avatar answered Oct 04 '22 23:10

August Lilleaas


You could use Immutable.js. It is a library that Facebook made to work with react/flux. https://facebook.github.io/immutable-js/

We have been stumped by this a couple of times.

shouldComponentUpdate(nextProps) { 
    return this.props.myObj !== nextProps.myObj
}

Many times, some value inside the myObj object would change. However the above function would return false. The reason being that both this.props.myObj and nextProps.myObj were referencing/pointing to the same object.

By implementing Immutable.js, the data will always be passed down as clones (instead of referencing the same object, they will actually be separate objects).

It re-enforces flux's uni-directional flow. You will never be able to (accidentally) modify the original data that is being passed into your component (whether as props or from a flux store).

This also enables you to use a PureRenderMixin – which automatically stops the render if the values of state/props have not changed. This could help with performance.

like image 30
Federico Avatar answered Oct 04 '22 22:10

Federico