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

Javascript REACT REDUX:更新深嵌套对象中的值,javascript,json,reactjs,redux,Javascript,Json,Reactjs,Redux,我试图更新深嵌套对象中存储的值。它包含许多信息,模式是固定的。我试图复制对象,然后从输入返回具有更新值onChange的对象。但是,我无法成功地复制完整的树并返回更新的内容 演示: 该对象看起来像: content: { label: "Label", templates: [ { name: "example", type: "the type", items: [ { ke

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

演示

该对象看起来像:

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"
              }
            }
          }
        ]
      }
    ]
  }
减速器:

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);
import React,{PureComponent}来自“React”;
从“react redux”导入{connect};
从“/actions”导入{updateValue};
类页扩展了PureComponent{
render(){
const{content,templateKey,itemKey}=this.props;
返回(
{content.templates[templateKey].items[itemKey].properties.text.value}
this.props.updateValue(e.target.name,e.target.value)}
/>
);
}
}
常量mapStateToProps=状态=>({
内容:state.content,
templateKey:state.templateKey,
itemKey:state.itemKey
});
const mapDispatchToProps=调度=>({
updateValue:(键,值)=>分派(updateValue(键,值))
});
导出默认连接(
MapStateTops,
mapDispatchToProps
)(第页);

是否尝试使用排列操作将旧嵌套对象的当前状态的内容填充到新对象中,然后添加新的更新值?这样,不更改的字段保持不变。这并不确切,但在您的情况下,我会在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 action creator中,您只需传入新内容对象,该对象保留您不更新的旧值,并包含您所做更改的新值。您可以在onChange处理程序的page.js上启动此方法。

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

与:

  const content = state.content;
  content.templates[state.templateKey].items[
    state.itemKey
  ].properties.text.value =
    action.value;
关键是返回内容的新引用,因为您正在将一个处于映射状态的选择器应用于内容本身上的道具(或者,作为更好的解决方案,组合这样的缩减器以包含多个缩减器),即:


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

对于这样的深度嵌套数据,您可以在树的每个深度使用排列语法,直到找到要更改的内容。对于数组,可以使用
slice
创建数组的副本,而无需对其进行变异

这些资源将帮助您更好地理解这一点

假设您的reducer有一个要更新的模板的索引,以及一个要在该模板中更新的项的索引。您的代码可能如下所示:

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)
    ]
  }
正如您所看到的,当您处理这样的嵌套数据时,它变得非常混乱。建议对嵌套数据进行规范化,以使还原程序减少查找要更改内容的工作量


这是您的更新代码沙盒:

您需要使用更新的值创建一个全新的内容树。您不应该对该项进行变异。这仍然是对内容引用中的
items[itemKey].properties.text.value
进行变异,因为扩展语法只创建了一个浅副本。@ZackTanner创建这样一个大对象的不可变副本的最佳方法是什么?我发现使用map/filter来执行数组更新更容易。语法看起来容易多了。在更新多个实例的情况下,我认为这是有意义的。但是,如果只更新单个数组元素,则必须在
map
/
filter
中设置一个条件,以便只更新目标索引,因此在这一点上,您似乎可以只进行切片。我同意,虽然这看起来非常难看。这会伤害我的大脑,但效果最好,符合Redux指南。默罕默德的建议是可行的,但这是一个肤浅的复制品,不被推荐。这绝对是一个视觉障碍。考虑不同的建模你的商店,让你很容易在未来作出更新。IE,想一想如何尽可能扁平化数据结构,同时保留相同的含义。@ZackTanner好的,太好了。我会调查的。你知道一个好例子吗?我必须将模板obj传输回db,所以我正在摸索打破这个想法。
 const content = {...state.content};
  content.templates[state.templateKey].items[
    state.itemKey
  ].properties.text.value =
  action.value;
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)
    ]
  }