REACT REDUX:更新深层嵌套对象中的值

时间:2018-09-22 17:39:12

标签: javascript json reactjs redux

我正在尝试更新存储在深层嵌套对象中的值。它包含许多信息,并且架构是固定的。我试图复制对象,然后从输入中返回具有更新值onChange的对象。但是,我无法成功正确地复制完整的树并返回更新的内容。

演示https://codesandbox.io/s/4j7x8jlk9w

对象看起来像:

content: {
    label: "Label",
    templates: [
      {
        name: "example",
        type: "the type",
        items: [
          {
            key: "key1",
            properties: {
              text: {
                label: "The Label 1",
                value: "The Value 1"
              },
              color: {
                label: "Color",
                value: "#123"
              }
            }
          },
          {
            key: "key2",
            properties: {
              text: {
                label: "The Label 2",
                value: "The Value 2"
              },
              color: {
                label: "Color",
                value: "#456"
              }
            }
          }
        ]
      }
    ]
  }

Reducer:

case "UPDATE_VALUE":
      const content = state.content.templates[state.templateKey].items[
        state.itemKey
      ].properties.text.value =
        action.value;

      return { ...state, content };

    default:
      return state;
  }

组件:

import React, { PureComponent } from "react";
import { connect } from "react-redux";

import { updateValue } from "./actions";

class Page extends PureComponent {
  render() {
    const { content, templateKey, itemKey } = this.props;

    return (
      <div>
        <h1
          style={{
            color:
              content.templates[templateKey].items[itemKey].properties.color
                .value
          }}
        >
          {content.templates[templateKey].items[itemKey].properties.text.value}
        </h1>
        <input
          name={content.templates[templateKey].items[itemKey].key}
          value={
            content.templates[templateKey].items[itemKey].properties.text.value
          }
          onChange={e => this.props.updateValue(e.target.name, e.target.value)}
        />
      </div>
    );
  }
}

const mapStateToProps = state => ({
  content: state.content,
  templateKey: state.templateKey,
  itemKey: state.itemKey
});

const mapDispatchToProps = dispatch => ({
  updateValue: (key, value) => dispatch(updateValue(key, value))
});

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Page);

3 个答案:

答案 0 :(得分:0)

使用这样的深层嵌套数据,您可以在树的每个深度处使用扩展语法,直到找到要更改的对象为止。对于数组,可以使用slice来创建数组的副本而无需对其进行更改。

https://redux.js.org/recipes/structuringreducers/immutableupdatepatterns#correct-approach-copying-all-levels-of-nested-data

https://redux.js.org/recipes/structuringreducers/immutableupdatepatterns#inserting-and-removing-items-in-arrays

将是帮助您更好地理解这一点的资源。

让我们为您的减速器提供在该模板中要更新的模板的索引和要更新的项目的索引。您的代码可能如下所示:

return {
    ...state,
    templates: [
      ...state.templates.slice(0, templateIndex),
      {
        ...state.templates[templateIndex],
        items: [
          ...state.templates[templateIndex].items.slice(0, itemIndex),
          {
            ...state.templates[templateIndex].items[itemIndex],
            value: action.value 
          },
          ...state.templates[templateIndex].items.slice(itemIndex)
        ]
      },
      ...state.templates.slice(templateIndex)
    ]
  }

如您所见,当您像这样处理嵌套数据时,它变得非常混乱。建议您对嵌套数据进行规范化,以使化简器减少要做的工作来查找要更改的内容。

这是您更新的代码和框:https://codesandbox.io/s/w77yz2nzl5

答案 1 :(得分:-1)

您是否尝试过使用传播操作将旧嵌套对象的当前状态内容填充到新对象中,然后在其后添加新的更新值?这样,您无需更改的字段将保持不变。这不是确切的,但是我要在您的情况下在page.js上创建一个方法,例如:

createNewValue = (e) => {
  const { content, templateKey, itemKey } = this.props;
  let newValues = {...content }
  newValues.templates[templateKey].items[itemKey].properties.text.value = e.target.value
  this.props.updateValue(newValues)
}

然后在updateValue动作创建器中,您只需传递一个新的内容对象,该对象将保留您未更新的旧值,并包括您要进行的更改的新值。您可以在onChange处理程序中的page.js上触发此方法。

答案 2 :(得分:-1)

首先,您没有返回正确的内容对象,因为您正在返回update的值,因此您应该更新 :

 const content = state.content.templates[state.templateKey].items[
    state.itemKey
  ].properties.text.value =
    action.value;

与:

  const content = state.content;
  content.templates[state.templateKey].items[
    state.itemKey
  ].properties.text.value =
    action.value;

关键是返回一个新的内容引用,因为您正在将处于地图状态的选择器应用于内容本身上的props(或者,作为更好的解决方案,将这样的reducer组成为包含多个reducer),即:

 const content = {...state.content};
  content.templates[state.templateKey].items[
    state.itemKey
  ].properties.text.value =
  action.value;

或将选择器应用于所需的值(即更精细的选择器)