【问题标题】:Replace object in array on react state在反应状态下替换数组中的对象
【发布时间】:2017-02-14 18:52:34
【问题描述】:

这个问题可能有点偏向“最佳实践”问题,但请多多包涵。

这是我的一部分状态:

this.state = {
  typeElements: {
    headers: [
        {
          name: "h1",
          size: 70,
          lineHeight: 1.25,
          kearning: 0,
          marginAfter: 0
        }, {
          name: "h2",
          size: 70,
          lineHeight: 1.25,
          kearning: 0,
          marginAfter: 0
        }, {
          name: "h3",
          size: 70,
          lineHeight: 1.25,
          kearning: 0,
          marginAfter: 0
        }...

我需要做的是在 headers 数组的给定索引处替换对象。

我不知道如何使用 this.setState(headers[1] = {obj}) 中的 setState 方法来做到这一点——但这显然是无效的。我当前的方法是创建一个新数组并像这样破坏旧数组:

_updateStyle(props) {
  let newState = Object.assign({}, this.state)
  newState.typeElements.headers[props.index] = props
  this.setState(newState)
};

对于我的小型 hacky 项目,我想这还可以,但我觉得这是超级繁重的,并且会很快导致任何规模的性能问题。

【问题讨论】:

    标签: javascript arrays reactjs ecmascript-6 immutability


    【解决方案1】:

    更新:由于这个答案仍然得到了支持,请注意下面的上一个答案已经过时了现代 JavaScript 和 React。 “更新”插件现在是旧版,可以使用 "immutability-helper" 代替。

    React 文档还提到了为什么 immutability is important 如此避免改变状态。对于不可变更新,您可以使用Object.assign()spread syntax,这需要对每一级嵌套进行,例如在本例中嵌套的headers 对象及其数组元素。在这个特定的示例中,我们可以使用数组索引作为键,因此还可以使用扩展运算符对数组进行浅层克隆,并在克隆数组中的给定索引处分配一个新对象作为值。

    _updateStyle (props) {
      const { typeElements } = this.state;
      const updatedHeaders = [...typeElements.headers];
      updatedHeaders[props.index] = props;
      this.setState({
        ...this.state,
        typeElements: {
          ...typeElements,
          headers: updatedHeaders
        }
      ));
    }
    

    另一个不需要展开语法并且如果我们不使用数组索引来查找我们想要替换的对象时需要的解决方案是使用array.map 创建一个新数组并返回新对象而不是给定索引处的旧的。

      const updatedHeaders = typeElements.headers.map((obj, index) => {
        return index === props.index ? props : obj;
      });
    

    Redux 文档中的类似示例也解释了"immutable update patterns"

    React 对此有一些不变性助手,在 文档:https://facebook.github.io/react/docs/update.html

    在您的情况下,您可以使用 $splice 命令删除一项并 在给定索引处添加新的,例如:

    _updateStyle (props) {
      this.setState(update(this.state.typeElements, 
        { $splice: [[props.index, 1, props]] }
      ));
    }
    

    【讨论】:

    • aha,我知道这些,但不确定如何实现,所以它进入 setState 方法? “1”有什么作用?那是新对象的插入点吗?如果是这样的话,我不能再使用 props.index 了吗?
    • 查看array.splice() 方法developer.mozilla.org/nl/docs/Web/JavaScript/Reference/… 你会明白...数组保存了传递给splice 方法的参数,所以这个例子中的“1”是deleteCount。
    【解决方案2】:

    更好地解释如何实现这一点。

    • 首先,在状态数组中找到要替换的元素的索引。
    • 第二,update该索引处的元素
    • 第三,使用新系列致电setState
    import update from 'immutability-helper';
    
    // this.state = { employees: [{id: 1, name: 'Obama'}, {id: 2, name: 'Trump'}] } 
    
    updateEmployee(employee) {
        const index = this.state.employees.findIndex((emp) => emp.id === employee.id);
        const updatedEmployees = update(this.state.employees, {$splice: [[index, 1, employee]]});  // array.splice(start, deleteCount, item1)
        this.setState({employees: updatedEmployees});
    }
    

    【讨论】:

      【解决方案3】:

      使用immutability-helper

      你可以在那里找到很好的例子

      【讨论】:

        【解决方案4】:

        Object.Assign 使用浅拷贝,而不是深拷贝。

        请注意,在您的示例中,Object.assign({}, this.state) 仅复制到 state 的最近子代的链接,即到 typeElements,但不会复制 headers 数组。

        在 ES6 中有一个用于 Object.Assign 的语法糖 ...,Babel 有一个特殊的插件 transform-object-rest-spread

        let newHeaders = { ...this.state.typeElements.headers};
        newHeaders[index] = props.Something;
        
        let newState = {...state, typeElements: { newHeaders }};
        

        【讨论】:

        • 真的吗?它实际上也为我复制了标题。你能详细说明一下吗?
        • @motleydev Object.assign() 方法仅将可枚举和 拥有 属性从源对象复制到目标对象 (MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…)。这意味着它只复制最近对象的引用或基元(数字、字符串)的值。你在你的实现中得到了旧的数组,如果你改变它,那么它也会影响其他正在使用它的地方。
        猜你喜欢
        • 1970-01-01
        • 2018-06-16
        • 1970-01-01
        • 2018-05-18
        • 2021-09-20
        • 2019-01-10
        • 1970-01-01
        • 2021-12-26
        • 1970-01-01
        相关资源
        最近更新 更多