【问题标题】:Multiple components catches the save store event多个组件捕获保存存储事件
【发布时间】:2015-06-23 17:23:15
【问题描述】:

我有一个输入组件,它获取 url 作为输入并解析它(resolve=以某种方式获取该 url 的预览):

var resolver = require('resolver');
var UrlResolver = React.createClass({
    statics: {
        storeListeners: {
            onResolverStore: [ ResolverStore ]
        }
    },
    getInitialState: function() {
        return { value: '', resolves: [] };
    },
    onResolverStore: function(data) {
        var resolves = [].concat(this.state.resolves, data);
        this.setState({ resolves: resolves });
    },
    handleChange: function(event) {
        var value = event.target.value;
        this.setState({ value: value });
        this.executeAction(resolver, value);
    },
    render: function () {
        return (
            <input value={ this.state.value } onChange={ this.handleChange } />
            {
                this.state.resolves.map(function(data) {
                    return <ResolveView data={ data } />;
                });
            }
        );
    }
});

如您所见,UrlResolver 等待 ResolverStore 上的更改。当输入发生变化时,可能会发生这种变化。 我的问题是当我有 10 个 UrlResolvers 时。在这种情况下,对一个输入的任何更改都会更改 ResolverStore,这将触发 10 个不同的事件,因此 10 个不同的 setStates 将导致 10 次重新渲染。所有这一切,而只有一个输入应处理此更改。这样 9 个组件也会添加一个不属于它们的解析数据。

这种需求有什么解决方案?

【问题讨论】:

    标签: javascript reactjs reactjs-flux flux fluxible


    【解决方案1】:

    您似乎正在使用将数据从存储推送到组件的 Flux 实现,该实现获取存储正在包装的所有状态并将其推送到组件状态。当不同的组件只对该数据的一个子集感兴趣,或者如果需要先查询数据时,这就会成为一个问题。

    我对 Flux 的看法是,数据应该由组件从商店中提取,而不是相反。这样一来,您就可以在组件中完全灵活地处理您感兴趣的数据,而不会丢失单向数据流。

    一个例子是这样的:

    var MyComponent = React.createClass({
      getInitialState() {
        return {
          items: []
        };
      },
      componentWillMount() {
        MyStore.on('change', this.loadItems);
        this.loadItems();
      },
      componentWillUnmount() {
        MyStore.off('change', this.loadItems);
      },
      loadItems() {
        MyStore.find(/* search params or find by id goes here */)
          .then(items => this.setState({items: items}));
      },
      render() {
        return (
          <ul>
            {this.state.items.map(item => <li>{item}</li>)}
          </ul>
          <button onClick={e => MyActions.add()}>Add new stuff</button>
        );
      }
    });
    

    如您所见,由组件向存储区查询该组件感兴趣的任何数据,但该组件仍订阅存储区中状态的任何更改。在此示例中,它是一个项目数组,但它也可以是通过 id 作为道具传递给组件的单个项目。

    如果所有状态都在内存中,推送存储的整个状态效果很好,但对于由 REST API 或 WebSQL/IndexedDB 数据库支持的存储,我发现此模型更简单。

    编辑

    为了进一步阐明为什么我更喜欢从商店拉取而不是推送到组件。通过让组件只获取它需要的数据而不是让商店将它的整个状态推送给你,更容易查看它是否值得重新渲染。

    在我的示例中,它会在调用操作后立即在每个组件实例中调用 loadItems,但是通过从存储中提取,您可以通过检查组件的数据是否让组件短路兴趣变了。这可以通过让您的商店使用不可变数据来完成,这意味着您可以使用引用相等来确定某些内容是否已更改。另一种选择是传递更改事件中实际更改的内容。然后组件可以检查更改项的 id 是否是它关心的东西,如果不是,则跳过重新渲染。

    我建议不要进行此类检查,因为它很容易陷入组件的陷阱,几乎不知道它是否应该重新渲染。如果您改为从商店返回不可变的列表/记录/地图,这只是对返回的内容与之前必须知道的组件感兴趣的内容是否已更改的内容进行相等性检查。

    【讨论】:

    • 我不明白。你和我有同样的问题。如果屏幕中有多个 MyComponent 组件,如果一个组件会触发 MyActions.add(),那么所有组件都会运行 loadItems()。在我的示例中,唯一希望 loadItems() 运行的组件是调用 MyActions.add() 的第一个组件。
    • 我在对帖子的编辑中进行了一些扩展,希望能澄清一下。
    • 我想避免其他回调被执行。我可以想出很多方法来过滤结果,但我不希望发生其他回调。看来我不需要在这里使用通量动作。你怎么看?
    • 另一种选择是不仅仅拥有 Flux 实现通常只有的 change 事件。您可以举办任何可能适合您需求的活动。您可以订阅 change 事件并传入一个 id,告诉商店仅在具有该 id 的对象发生更改时才调用侦听器。
    猜你喜欢
    • 1970-01-01
    • 2021-05-14
    • 1970-01-01
    • 2018-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多