PureRenderMixin打破绑定事件处理程序

时间:2015-05-15 14:03:16

标签: javascript reactjs

我尝试使用PureRenderMixin尽可能多地优化我的React组件,并确保所有传递的道具都是不可变的,并且不会从渲染更改为渲染。

我遇到的一个问题是改变作为props传递的绑定事件处理程序的情况。更具体地说,我有一个渲染和一系列孩子:

在家长中:

onChange: function(key, data) {
    ...
},

render: function() {
        return this.state.array.map(function(object, index) {
            return <Child key={object.key} onChange={this.onChange.bind(key)} stuff={object.stuff}/>
        }
    }) 
}

在孩子:

...
mixins: [PureRenderMixin]
...

事实证明,PureRenderMixin总是触发Child内部的更新,因为它的onChange prop总是一个新实例(即使它总是绑定到同一个键)。也就是说,每次调用anyFunction.bind(123)都会生成一个新值(即它们不是===)。

人们如何处理这个问题?以下是我能想到的两种方式:

  1. 传递未绑定的onChange处理程序,单独传递密钥,让孩子用密钥本身调用onChange - 这有点乱,因为它给孩子增加了不必要的复杂性。

  2. 在父级中缓存绑定的onChange处理程序 - 为父级添加复杂性(将它们存储在地图中?还是存储在每个处理程序中?)

  3. 让PureRenderMixin跳过on*道具 - 当这些处理程序确实发生变化并在孩子的层次结构中进一步传递时会中断。

  4. 我似乎无法找到一个优雅的解决方案。我错过了什么吗?

    由于

3 个答案:

答案 0 :(得分:1)

您还可以记住bind的结果:

function memoizedBind(fn, context) {
  var cache = fn.__memoize_cache;

  if (!cache) {
    cache = fn.__memoize_cache = {};
  }

  var key = getKey(context); // if context is primitive can be just `context`

  var cachedFn = cache[key];

  if (!cachedFn) {
    cachedFn = cache[key] = fn.bind(context);
  }

  return cachedFn;
}

然后使用memoizedBind代替bind。它将返回相同函数和上下文的相同结果。

答案 1 :(得分:0)

我认为#2是合适的答案。我不认为它增加了复杂性,Child已经拥有了密钥,所以为什么不让他传递它。

Child = React.createClass({
  handleChange: function(newData) {
    this.props.onChange(this.props.key, newData); 
  },

  render: function(){
    ...
  }
});

答案 2 :(得分:0)

您可以创建一个中间组件,其唯一目的是将参数部分应用于onChange处理程序方法。

const PartialApply = React.createClass({
  _handler(...args) {
    this.props.handler.apply(null, this.props.partialArgs.concat(args));
  },

  shouldComponentUpdate(nextProps) {
    return (
      shallowEqual(this.props, nextProps) &&
      shallowEqual(this.props.partialArgs, nextProps.partialArgs)
    );
  },

  render() {
    return React.cloneElement(React.Children.only(this.props.children), {
      [this.props.handlerName]: this._handler,
    });
  },
});

然后,使用它:

render() {
  return (
    <div>
      {this.state.array.map(object => {
        return (
          <PartialApply
            key={object.key}
            handler={this._handleChange}
            handlerName="onChange"
            partialArgs={[object.key]}
          >
            <Child />
          </PartialApply>
        );
      })}
    </div>
  )
}