将Flux存储中的Immutable.js映射与内部React组件状态合并

时间:2015-05-19 15:21:32

标签: javascript reactjs reactjs-flux flux immutable.js

我一直在慢慢转换我的React + Flux应用程序以使用Immutable.js数据结构。我使用Flux的原始的vanilla FB实现。

我遇到的一个问题是将组件状态与从Flux商店收到的状态混合

我将所有重要的业务逻辑状态保留在商店中。但我的规则是将UI相关状态保持在组件中。例如,如果打开下拉菜单,商店不需要关心,对吗?

当在组件中执行操作时,会出现问题,该组件会更改同一组件的存储中的状态。假设我们有一个打开下拉菜单的组件。从该下拉菜单中选择一个项目。该操作传播到ItemStore,商店发出更改,组件从商店获取新状态。

_onChange() {
  this.setState(this._getState());
}
_getState() {
  if(this.state === undefined) {
    return {
      data: Immutable.Map({
        selectedItem: ItemStore.getSelectedItem(),
        items: ItemStore.getItems(),
        menuIsOpen: false
      })
    };
  }
  return {
    data: this.state.data.merge(Immutable.Map({
      selectedItem: ItemStore.getSelectedItem(),
      items: ItemStore.getItems(),
      menuIsOpen: this.state.data.get("menuIsOpen")
    }))
  };
}

同时,在组件中,单击下拉菜单项会发出旧式onClick事件。我有一个_handleClick函数,它使用setState来关闭下拉菜单(本地状态)。

_handleClick(event) {
  event.preventDefault();
  this.setState({
    data: this.state.data.set("menuIsOpen", !this.state.data.get("menuIsOpen"))
  });
}

问题是_handleClick_getState之后很快被调用,因为它没有this.state.data的更新副本。因此,在组件的render方法中,this.state.data.get("selectedItem")仍会显示之前选择的项目。

当我使用POJO执行此操作时,React的setState似乎正确地批量处理所有内容,因此它从来不是问题。但是我不希望有一个不属于Immutable.Map的状态,因为我想利用“纯粹的”render ing。然而,我不想在我的商店中引入UI状态,因为我觉得这可能会很快变得混乱。

有没有办法解决这个问题? 或者在单个组件中合并本地Immutable.Map状态和Immutable.Map存储状态只是一种不好的做法?

相关:我不喜欢在我的if(this.state === undefined)方法中设置初始本地menuIsOpen状态的样板_getState逻辑。 这可能表明我正在尝试做一些不正确的事情。

2 个答案:

答案 0 :(得分:3)

您可以将回调传递给setState以将原子更新排入队列。

_onChange() {
  this.setState(state => this._getState(state));
}
_getState(state) {
  if(state === undefined) {
    return {
      data: Immutable.Map({
        selectedItem: ItemStore.getSelectedItem(),
        items: ItemStore.getItems(),
        menuIsOpen: false
      })
    };
  }
  return {
    data: state.data.merge(Immutable.Map({
      selectedItem: ItemStore.getSelectedItem(),
      items: ItemStore.getItems(),
      menuIsOpen: state.data.get("menuIsOpen")
    }))
  };
}

关于您的相关观点,您可能需要查看getInitialState

答案 1 :(得分:2)

单击(触发存储操作,关闭菜单)时,为什么会发生2个单独的操作?相反,您可以说,当他们单击菜单项时,我们需要关闭菜单项,并向商店提醒新值。

_handleClick(event) {
  event.preventDefault();
  this.setState({
    data: this.state.data.set("menuIsOpen", false)
  }, function() {
    alertTheSelectionChange(selectedItem)
  });
}