React: use or not to use Immutable.js

喜你入骨 提交于 2019-12-13 12:35:04

问题


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:

  1. It has clear interface and makes it clear for your colleges that the state should be cloned each time
  2. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!