将 React 的 shouldComponentUpdate 与 Immutable.js 游标一起使用

Using React's shouldComponentUpdate with Immutable.js cursors

我无法弄清楚如何短路渲染分支 使用 Immutable.js 游标的 React 组件树。

举个例子:

import React from 'react';
import Immutable from 'immutable';
import Cursor from 'immutable/contrib/cursor';

let data = Immutable.fromJS({
  things: [
    {title: '', key: 1},
    {title: '', key: 2}
  ]
});

class Thing extends React.Component {
  shouldComponentUpdate(nextProps) {
    return this.props.thing.deref() !== nextProps.thing.deref();
  }

  handleChangeTitle(e) {
    this.props.thing.set('title', e.target.value);
  }    

  render() {
    return <div>
      <input value={this.props.thing.get('title')} 
        onChange={this.handleChangeTitle.bind(this)} />
    </div>;
  }
}

class Container extends React.Component {
  render() {
    const cursor = Cursor.from(this.props.data, 'things', newThings => {
      data.set('things', newThings);
      renderContainer();
    });

    const things = cursor.map(thing => (
      <Thing thing={thing} key={thing.get('key')} />
    ));

    return <div>
      {things}
    </div>;
  }
}

const renderContainer = () => {
  React.render(<Container data={data} />, document.getElementById('someDiv'));
};

假设我更改了第一个 Thing 的标题。只有第一个 Thing 会呈现 新标题和第二个 Thing 不会 re-render 由于 shouldComponentUpdate。但是,如果我更改第二个 Thing 的标题, 第一个 Thing 的标题将返回到 '',因为第二个 Thing 的光标 仍然指向旧版本的根数据。

我们在 Container 的每个渲染器上更新光标,但不更新的那些 由于 shouldComponentUpdate 而渲染也没有得到更新后的新光标 根数据。我能看到使游标保持最新的唯一方法是删除 本例中 Thing 组件中的 shouldComponentUpdate

有没有办法将此示例更改为使用 shouldComponentUpdate 使用快速引用 相等性检查但也保持游标更新?

或者,如果这不可能,您能否概述一下通常如何使用游标 + React 组件以及仅渲染具有更新数据的组件?

我更新了您的代码,请参阅内联评论:

class Thing extends React.Component {
  shouldComponentUpdate(nextProps) {
    return this.props.thing.deref() !== nextProps.thing.deref();
  }

  handleChangeTitle(e) {
    // trigger method on Container to handle update
    this.props.onTitleChange(this.props.thing.get('key'), e.target.value);
  }    

  render() {
    return <div>
      <input value={this.props.thing.get('title')} 
        onChange={this.handleChangeTitle.bind(this)} />
    </div>;
  }
}

class Container extends React.Component {
  constructor() {
    super();
    this.initCursor();
  }

  initCursor() {
    // store cursor as instance variable to get access from methods
    this.cursor = Cursor.from(data, 'things', newThings => {
      data = data.set('things', newThings);
      // trigger re-render
      this.forceUpdate();
    });
  }

  render() {
    const things = this.cursor.map(thing => (
      <Thing thing={thing} key={thing.get('key')} onTitleChange={this.onTitleChange.bind(this)} />
    ));

    return <div>
      {things}
    </div>;
  }

  onTitleChange(key, title){
    // update cursor to store changed things
    this.cursor = this.cursor.update(x => {
      // update single thing
      var thing = x.get(key - 1).set('title', title);
      // return updated things
      return x.set(key - 1,thing);
    });
  }
}

const renderContainer = () => {
  React.render(<Container data={data} />, document.getElementById('someDiv'));
};