将 Flux 存储中的 Immutable.js 个映射与内部 React 组件状态合并
Merging Immutable.js Maps in Flux store with internal React component state
我一直在慢慢地将我的 React+Flux 应用程序转换为使用 Immutable.js 数据结构。我使用 Flux 的原始香草 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 的状态,因为我想利用 "pure" render
ing。但是我不想在我的商店中引入 UI 状态,因为我觉得那样很快就会变得一团糟。
有什么办法可以解决这个问题吗? 或者将本地 Immutable.Map 状态和 Immutable.Map 存储状态合并到单个组件中只是一种不好的做法?
相关:我不喜欢在我的 _getState
方法中设置初始本地 menuIsOpen
状态的样板 if(this.state === undefined)
逻辑。 这可能表明我正在尝试做一些不正确的事情。
您可以将回调传递给 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
。
为什么单击时会出现 2 个单独的操作(触发存储操作、关闭菜单)?相反,您可以说,当他们单击菜单项时,我们需要关闭菜单项,并提醒商店新值。
_handleClick(event) {
event.preventDefault();
this.setState({
data: this.state.data.set("menuIsOpen", false)
}, function() {
alertTheSelectionChange(selectedItem)
});
}
我一直在慢慢地将我的 React+Flux 应用程序转换为使用 Immutable.js 数据结构。我使用 Flux 的原始香草 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 的状态,因为我想利用 "pure" render
ing。但是我不想在我的商店中引入 UI 状态,因为我觉得那样很快就会变得一团糟。
有什么办法可以解决这个问题吗? 或者将本地 Immutable.Map 状态和 Immutable.Map 存储状态合并到单个组件中只是一种不好的做法?
相关:我不喜欢在我的 _getState
方法中设置初始本地 menuIsOpen
状态的样板 if(this.state === undefined)
逻辑。 这可能表明我正在尝试做一些不正确的事情。
您可以将回调传递给 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
。
为什么单击时会出现 2 个单独的操作(触发存储操作、关闭菜单)?相反,您可以说,当他们单击菜单项时,我们需要关闭菜单项,并提醒商店新值。
_handleClick(event) {
event.preventDefault();
this.setState({
data: this.state.data.set("menuIsOpen", false)
}, function() {
alertTheSelectionChange(selectedItem)
});
}