shouldComponentUpdate + deepEqual 和数组

shouldComponentUpdate + deepEqual and arrays

问题: shouldComponentUpdate 使用 this.state 检索以前的状态,如果您在 UserList 保持对数组的引用,那将不起作用,并在 UserStore.

处更新数组实体

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;
};

UserList.react.js

class UserList extends React.Component {

    constructor(props) { 
        super(props);
        this._onChange = this._onChange.bind(this);
    }

    componentWillMount() {
        UsersStore.addChangeListener(this._onChange);
    }

    _onChange() {            
        this.setState({userList: UsersStore.getState()});
    }
}

module.exports = PureRenderMixin(UserList);

UsersStore.js

......
getState() { return _userList; }

switch(action.type) {
   case ActionTypes.UPDATE_USER_FLAG:
       //!!!!!!!!!!!!!!
       //PROBLEM: since UserList.react keep userList reference, there is no way to retrieve previous state inside shouldComponentUpdate
       _userList[action.index].flag = action.flag;
       UsersStore.emitChange();
       break;
}

@taggon 解决方案

感谢 taggon,现在我知道如何使 shouldComponentUpdate 保持对先前状态的引用:

UsersStore.js

......
getState() { return _userList; }

switch(action.type) {
   case ActionTypes.UPDATE_USER_FLAG:
       //SOLUTION: copy an array, so there will be two versions of _userList[action.index]
       _userList =  _.map(_userList, _.clone);

       _userList[action.index].flag = action.flag;
       UsersStore.emitChange();
       break;
}

如果你的 props 是不可变的,你可以安全轻松地通过引用比较数据。你可以看看 immutablejs

class ProductStore extends ReduceStore {
  getInitialState() {
    return Immutable.OrderedMap({1: new Product('react', 'flux'), 2: new Product('angular', 'mvc')});
  }
  reduce (state, action) {
    switch (action.type) {
      case 'product/item-selected':
        return state.map((product)=> {
          return product.set('selected', product.id === action.id); 
        });
      case 'product/search':
        let alldata = this.getInitialState();
        return alldata.filter((product)=> {
          return product.name.indexOf(action.value) !== -1;
        });
      default:
        return state;
    }
  }
}


export default class ProductDetail extends Component {

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

  render() {
    const {product} = this.props;

    return (
      <div className="product-detail">
        <div className="product-name">
          {product.name}
        </div>
        <div className="product-type">
          {product.type}
        </div>
      </div>
    );
  }
}

https://facebook.github.io/immutable-js/

我认为问题出在店里。 商店最好在其状态更改时创建另一个数组。

例如,将商店视为一个数组:

var store = [ ];

export default store;

您可能想像这样编写 add() 函数:

export function add(newItem) {
  store = [ ...store, newItem ];
  // or write this in es5
  store = store.concat([newItem]);

  // trigger change event or dispatch an action here
}

同样,remove()函数可以是:

export remove(index) {
  store = [ ...store.slice(0, index), ...store.slice(index+1) ];

  // trigger change event or dispatch an action here
}

这样一来,只要商店发生变化,商店就会取消引用组件的状态。这使得shouldComponentUpdate() return true.

希望对你有所帮助。