Javascript 反应:使用或不使用Immutable.js

Javascript 反应:使用或不使用Immutable.js,javascript,reactjs,immutable.js,Javascript,Reactjs,Immutable.js,在使用React.js时,我一直在寻找不可变的.js动机。据我所知,React.js应该保证属性的不变性。但是,我可以更改视图属性: immutable.js增强与PureRenderMixin的反应性能 purendermixin的来源: var ReactComponentWithPureRenderMixin = { shouldComponentUpdate: function(nextProps, nextState) { return !shallowEqual(this

在使用React.js时,我一直在寻找不可变的.js动机。据我所知,React.js应该保证属性的不变性。但是,我可以更改视图属性:


immutable.js增强与PureRenderMixin的反应性能

purendermixin的来源:

var ReactComponentWithPureRenderMixin = {
  shouldComponentUpdate: function(nextProps, nextState) {
    return !shallowEqual(this.props, nextProps) ||
           !shallowEqual(this.state, nextState);
  }
};

使用immutable.js比较shallowEqual中的两个对象非常快

在使用react pure rendering面对所有RAKE后,我想说的是
immutable.js
并不是提供deepEqual方法。要点是在每次修改时克隆状态。

为什么每次修改都需要克隆状态

在视图的状态只包含原始值之前,一切都很顺利。但有一种情况可能会发生,那就是数组或复杂对象树

假设您有一个视图
YourView
,它从存储
YourStore
获取一个数组。您不想使用
Immutable.js
,只需要包含Node.LS的
deepEqual
。例如:

purendermixin.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;
};
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);
......
getArray() { return _array; }

switch(action.type) {
   case ActionTypes.UPDATE_USER_FLAG:
       _array[action.index].value= action.flag; // BUG!!!
       YourStore.emitChange();
       break;
}
......
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;
}
YourView.react.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;
};
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);
......
getArray() { return _array; }

switch(action.type) {
   case ActionTypes.UPDATE_USER_FLAG:
       _array[action.index].value= action.flag; // BUG!!!
       YourStore.emitChange();
       break;
}
......
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;
}
YourStore.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;
};
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);
......
getArray() { return _array; }

switch(action.type) {
   case ActionTypes.UPDATE_USER_FLAG:
       _array[action.index].value= action.flag; // BUG!!!
       YourStore.emitChange();
       break;
}
......
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;
}
问题#1:shouldComponentUpdate返回false而不是true您希望
\u数组[action.index].value=action.flag
将更新
您的视图
,但它不会。这不是因为
shouldComponentUpdate
将返回
false

原因是数组只是一个引用,在
this.setState({array:YourStore.getArray()})
this.state.array(以前的状态)的时候也会更新。这意味着内部
应该组件更新
方法
此状态
下一状态
参考将指向同一对象

问题1#解决方案:

您需要在更新数组引用之前复制它(即在lodash的帮助下):

YourStore.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;
};
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);
......
getArray() { return _array; }

switch(action.type) {
   case ActionTypes.UPDATE_USER_FLAG:
       _array[action.index].value= action.flag; // BUG!!!
       YourStore.emitChange();
       break;
}
......
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;
}
问题#2:有时需要多次克隆阵列,代码出错

假设您需要更新
if
语句中
数组的多个值:

   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;
问题#2解决方案:使用
Immutable.js
。好处:

  • 它有清晰的界面,让你的大学明白每次都应该克隆这个州
  • 它有批量更新,所以您不必担心多次克隆阵列

  • AFAIK道具在v0.14中只是“不可变”(对象实际上是冻结的)。演示使用v0.13。编辑:是的:@FelixKling在v0.14中使用
    Immutable.js
    的动机是什么?@limelights是的,但是如果我可以使用习惯的js API,并且没有更多的理由切换-我不会使用
    Immutable.js
    如果希望值本身是不可变的,你会使用Immutable.js,例如,如果您将一个复杂的数据结构作为道具传递,并且该结构应该是不可变的。@FelixKling很好!这对我来说听起来很奇怪,React家伙只冻结道具引用,而不冻结其内部