简体   繁体   English

React Redux-减速器突变

[英]React Redux - Reducer Mutation

I have a reducer in which is mutating the state of the current state object by creating a shallow copy. 我有一个化简器,其中通过创建浅表副本来改变当前状态对象的状态。 The reducer works by updating a value in a stored object template then returning the full state object. 减速器的工作方式是更新存储的对象template的值,然后返回完整状态的对象。 The reducer does update the store and i am able to see the change on refresh with the persisted state being regenerated from localStorage. Reducer确实更新了存储,并且我能够看到刷新的更改,并且从localStorage重新生成了持久状态。 However, components subscribed using connect() do not get a re-render. 但是,使用connect()订阅的组件不会得到重新渲染。 How do i write this reducer the correct way to not mutate the state? 我该如何编写这种化简为不突变状态的正确方法?

the reducer: 减速器:

case UPDATE_STYLE:
  // Update the style value of the template
  let newState = {
    ...state
  }
  newState
      .templates[action.payload.selectedTemplateType]
      .content[action.payload.selectedTemplate]
      .template
      .styles[action.payload.styleKey][action.payload.fieldKey]
      .value = action.payload.value;

  // mutated state - not firing rerenders
  return newState;

firing the dispatch call (works): 触发调度电话(有效):

onStyleChange(styleKey, fieldKey, value){
    this.props.dispatch(
      updateStyle(selectedTemplateType, selectedTemplate, styleKey, fieldKey, value)
    );
  }

how the child component is connected (not rendering): 子组件的连接方式(不呈现):

const mapStateToProps = (state) => ({
  templates: state.templates.templates,
  selectedTemplateType: state.templateTypeSelection.selectedTemplateType,
  selectedTemplate: state.templateSelection.selectedTemplate
})

export default connect(mapStateToProps)(Index)

In your reducer, after the action is handled, the object state.templateTypeSelection.selectedTemplateType is the same object, even though some property deep inside the object changed. 在您的化state.templateTypeSelection.selectedTemplateType器中,处理完该动作后,即使对象深处的某些属性发生了变化,对象state.templateTypeSelection.selectedTemplateType是同一对象。 The other props have the same issue. 其他道具也有同样的问题。 So, to the component, the props are the same props, therefore the component is not rerendered. 因此,对于组件来说,道具是相同的道具,因此不会重新渲染组件。

There are a few solutions to this. 有一些解决方案。 1 To flatten your reducer state. 1使变径器状态变平。 (doesn't seem to work for you) 2 To deep copy the reducer state. (似乎不适合您)2要深度复制化简器状态。 3 When you do `mapStateToProps, try to map the innermost property, instead of the top level property. 3当执行mapStateToProps时,请尝试映射最里面的属性,而不是顶层属性。

For solution 2, what you can do is to create classes for the top level property of the reducer state. 对于解决方案2,您可以做的是为reducer状态的top属性创建类。 For example, you can create a class called TemplateTypeSelection for state.templateTyepeSelection . 例如,您可以为state.templateTyepeSelection创建一个名为TemplateTypeSelection的类。 When you handle the actions in the reducer, for the new state, you use new TemplateTypeSelection(prevState.templateTypeSelection) to create a new object for state.templateTypeSelection(You may want to use a constructor to do a shallow copy of the state.templateTypeSelection ). 在reducer中处理动作时,对于新状态,您可以使用new TemplateTypeSelection(prevState.templateTypeSelection)为state.templateTypeSelection创建一个新对象(您可能希望使用构造函数对state.templateTypeSelection进行浅表复制state.templateTypeSelection )。 Since, the state.templateTypeSelection is a new object now, your component should be able to rerender now. 由于state.templateTypeSelection现在是一个新对象,因此您的组件应该现在可以重新呈现。 The issue with this method is that, since every time you handle an action, you would create a new object, there might be some unnecessary rerender. 这种方法的问题在于,由于每次处理一个动作时,您都会创建一个新对象,因此可能会有一些不必要的重新渲染。 (This is just an example of how to do it. Prob not a good example, because in your component, you're connecting state.templateTypeSelection.selectedTemplateType , not state.templateTypeSelection . Hope you get the idea. ) (这只是一个示例,可能不是一个好例子,因为在您的组件中,您正在连接state.templateTypeSelection.selectedTemplateType ,而不是state.templateTypeSelection 。希望您能明白。)

Based on my experience, a combination of method 2 and 3 works best. 根据我的经验,方法2和3的组合效果最好。 Also, you may want to consider split the reducer into several reducers. 另外,您可能需要考虑将减速器分成几个减速器。

Another post already mentioned deep-copy as a quick solution (although you might lose performance). 另一篇文章已经提到深度复制是一种快速解决方案(尽管您可能会失去性能)。 However, even though it's a little tricky, the spread syntax ( ... ) might come to your rescue: 但是,尽管有些棘手,但是扩展语法( ... )可能会为您带来帮助:

let newState = { ...state, templates: {
  { ...state.templates, [action.payload.selectedTemplateType]: {
    { ...state.templates[action.payload.selectedTemplateType], content: {
      { ...state.templates[action.payload.selectedTemplateType].content, [ // and so on

At least, you'd only create new object for what has actually changed. 至少,您只会为实际更改创建新对象。

On that note: you might want to take a look at immutableJS which provides lots of helpers with this and is less prone to errors. 关于这一点:您可能想看看immutableJS,它为此提供了很多帮助,并且不太容易出错。 For instance, there is mergeDeep to help. 例如,有mergeDeep可以提供帮助。

When you don't want to do such a profound transition, there's also deep merge modules on npm (just google for "merge deep npm"); 当您不想进行如此深刻的过渡时,npm上还有深度合并模块(仅谷歌代表“ merge deep npm”); I just didn't actually try any of these... 我只是没有实际尝试任何这些...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM