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

Posted

技术标签:

【中文标题】将 Flux 存储中的 Immutable.js 映射与内部 React 组件状态合并【英文标题】:Merging Immutable.js Maps in Flux store with internal React component state 【发布时间】:2015-07-31 12:35:02 【问题描述】:

我一直在慢慢地将我的 React+Flux 应用程序转换为使用 Immutable.js 数据结构。我使用 Flux 的原始、普通的 FB 实现。

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

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

当一个组件中的某个动作改变了同一个组件的 store 中的状态时,问题就出现了。假设我们有一个带有打开的下拉菜单的组件。从该下拉菜单中选择一个项目。该操作传播到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 的状态,因为我想利用“纯”rendering。但是我不想在我的商店中引入 UI 状态,因为我觉得那样很快就会变得一团糟。

有什么办法可以解决这个问题吗? 或者将本地 Immutable.Map 状态和 Immutable.Map 存储状态合并到一个组件中只是一种不好的做法?

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

【问题讨论】:

【参考方案1】:

您可以将回调传递给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

【讨论】:

将函数传递给setState 是最终的解决方案;我完全不知道。另外,回复:getInitialState:因为我的组件使用的是 ES6 类,所以该方法不存在。由于我的组件类继承自 ImmutableFluxComponent 类,因此我在父类的 constructor 中设置了初始状态。但是现在我使用带有state 参数的_getState,我可以只传入一个空的Immutable.Map 来初始化state.data,而不需要if 语句。所以解决了2个问题。非常感谢。【参考方案2】:

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

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

【讨论】:

谢谢,这样也可以——我也不知道setState 的第二个回调参数。但在我的情况下,将函数传递给 setState 会更简洁一些。

以上是关于将 Flux 存储中的 Immutable.js 映射与内部 React 组件状态合并的主要内容,如果未能解决你的问题,请参考以下文章

在对象中设置值时 Redux 数据不重新呈现(Immutable.js)

Immutable.js 以及在 react+redux 项目中的实践

如何调用存储在 Store.js (React-Flux) 中的组件中的值

Immutable学习及 React 中的实践

API 令牌在 Flux (Redux) 存储中是不是安全?

Flux:处理模态窗口