问题
I've looked for Immutable.js motivation when using React.js. As I understand React.js should guarantee immutability of properties. But, I can change view properties in this demo:
var Hello = React.createClass({
render: function() {
var item = { prop: 128 };
return <Test item={item} />;
}
});
var Test = React.createClass({
render: function() {
this.props.item = -2; // Don't throw any error
return <div>{this.props.item}</div>; // Returns -2 instead of object
}
});
React.render(<Hello name="World" />, document.getElementById('container'));
Update: thanks to @FelixKling, now I know that properties will be immutable since React v0.14 (which is comming soon).
Question: What's the motivation to use Immutable.js
with React v0.14
?
Update2: Can we really change parent's properties? in SubComponent
let somePropVal = { a: 1 };
<SubComponent someProp={somePropVal} />
//....Then in SubComponent
this.props.someProp.a = 2; // somePropVal.a still will be `1` at parent element
回答1:
After facing all rakes with react pure rendering I want to say that Immutable.js
is not about providing deepEqual method. The main point is to clone state on each modification.
Why you need to clone state on each modification?
Everything goes well till view's state consists of primitive values only. But one moment that may happen it be an array or complex object tree.
Suppose you have a view YourView
which takes an array from a store YourStore
. and you don't want to use Immutable.js
, just include Node.LS's deepEqual
. For instance:
PureRenderMixin.js
const deepEqual = require('deep-equal');
module.exports = function pureRenderMixin(Component) {
Component.prototype.shouldComponentUpdate = function(nextProps, nextState) {
return !deepEqual(this.props, nextProps) || !deepEqual(this.state, nextState);
};
return Component;
};
YourView.react.js
class YourView extends React.Component {
constructor(props) {
super(props);
this._onChange = this._onChange.bind(this);
}
componentWillMount() {
YourStore.addChangeListener(this._onChange);
}
_onChange() {
this.setState({array: YourStore.getArray()});
}
}
module.exports = PureRenderMixin(YourView);
YourStore.js
......
getArray() { return _array; }
switch(action.type) {
case ActionTypes.UPDATE_USER_FLAG:
_array[action.index].value= action.flag; // BUG!!!
YourStore.emitChange();
break;
}
Problem #1: shouldComponentUpdate return false instead of true you expect that _array[action.index].value = action.flag;
will update YourView
, but it doesn't. It doesn't because shouldComponentUpdate
will return false
.
The reason is that array is just a reference, and at the moment of this.setState({array: YourStore.getArray()})
this.state.array (previous state) is also updated. That means that inside shouldComponentUpdate
method this.state
& nextState
refs will point to the same object.
Problem 1# solution:
You need to copy array reference before update it (i.e. with help of lodash):
YourStore.js
......
getArray() { return _array; }
switch(action.type) {
case ActionTypes.UPDATE_USER_FLAG:
_array = _.cloneDeep(_array); // FIXED, now the previous state will differ from the new one
_array[action.index].value= action.flag; // BUG!!!
YourStore.emitChange();
break;
}
Problem #2: sometimes you need to clone _array multiple times, code get buggy
Suppose you need to update multiple values of _array
inside if
statements:
case ActionTypes.UPDATE_USER_FLAG:
// You can't clone the _array once, because you don't know which conditions will be executed
// Also conditions may not be executed at all, so you can't clone the _array outside of if statements
if (someCondition) {
_array = _.cloneDeep(_array);
_array[someIndex].value= action.flag;
}
if (anotherCondition) {
_array = _.cloneDeep(_array);
_array[anotherIndex].value= action.flag;
}
YourStore.emitChange();
break;
Problem #2 solution: use Immutable.js
. Benefits:
- It has clear interface and makes it clear for your colleges that the state should be cloned each time
- It has batch updates, so you shouldn't worry about cloning array multiple times
回答2:
immutable.js boost react perfomance with PureRenderMixin
Source of PureRenderMixin:
var ReactComponentWithPureRenderMixin = {
shouldComponentUpdate: function(nextProps, nextState) {
return !shallowEqual(this.props, nextProps) ||
!shallowEqual(this.state, nextState);
}
};
With immutable.js compare two objects in shallowEqual are very fast
来源:https://stackoverflow.com/questions/32719425/react-use-or-not-to-use-immutable-js