更新项目数组中的单个值 |反应还原

Posted

技术标签:

【中文标题】更新项目数组中的单个值 |反应还原【英文标题】:Update single value in item array | react redux 【发布时间】:2017-04-22 11:08:13 【问题描述】:

我有一个待办事项列表,如果用户单击“完成”,我想将数组中该项的状态设置为“完成”。

这是我的行动:

export function completeTodo(id) 
    return 
        type: "COMPLETE_TASK",
        completed: true,
        id
    

这是我的减速器:

case "COMPLETE_TASK": 
             return ...state,
                todos: [
                    completed: action.completed
                ]
             
        

我遇到的问题是新状态不再具有与所选项目上的待办事项相关联的文本,并且 ID 不再存在。这是因为我覆盖了状态并忽略了以前的属性吗?我的对象项 onload 如下所示:

Objecttodos: Array[1]
    0: Object
        completed: false
        id: 0
        text: "Initial todo"
    __proto__: Object
    length: 1
    __proto__: Array[0]
    __proto__: Object

如您所见,我要做的就是将完成的值设置为 true。

【问题讨论】:

【参考方案1】:

您需要转换您的 todos 数组以更新相应的项目。 Array.map 是最简单的方法:

case "COMPLETE_TASK":
    return 
        ...state,
        todos: state.todos.map(todo => todo.id === action.id ?
            // transform the one with a matching id
             ...todo, completed: action.completed  : 
            // otherwise return original todo
            todo
        ) 
    ;

有一些库可以帮助您进行这种深度状态更新。您可以在此处找到此类库的列表:https://github.com/markerikson/redux-ecosystem-links/blob/master/immutable-data.md#immutable-update-utilities

就我个人而言,我使用 ImmutableJS (https://facebook.github.io/immutable-js/),它通过其 updateInsetIn 方法解决了这个问题(对于具有大量键的大型对象和数组,它们比普通对象和数组更有效,但速度较慢小的)。

【讨论】:

感谢您的帮助 TomW 并感谢您对有关深度状态更新的库的引用。很好的帮助! 我看到您使用的是单行 if 语句,尽管我发现它很难阅读。当有 if 语句时,我知道减速器会爆炸。你能解释一下这里发生了什么,以及写这个逻辑的另一种方式吗? 当你没有从每个分支返回状态时,减速器'炸弹' - if 语句很好,只是你需要确保你'返回状态;'在if 之后。如果您查看下面的 TOGGLE_TODO 处理程序:redux.js.org/docs/basics/Reducers.html#handling-more-actions,您会看到一个与我的非常相似的示例,它使用 if 语句而不是 ?: - ?: 比中的 if 语句有一点好处它迫使你从每个分支返回一个值。【参考方案2】:

新状态不再具有与该待办事项关联的文本 选中的项目和ID都没有了,是不是因为我在 覆盖状态并忽略之前的属性?

是的,因为在每次更新期间,您都分配了一个带有 只有一个completed 的新数组,并且该数组不包含任何以前的值。所以更新数组后将没有以前的数据。这就是为什么更新后 text 和 id 不存在的原因。

解决方案:

1- 使用array.map 找到正确的元素然后更新值,像这样:

case "COMPLETE_TASK":
    return 
        ...state,
        todos: state.todos.map(todo => 
            todo.id === action.id ?  ...todo, completed: action.completed  : todo
        ) 
    ;

2- 使用array.findIndex 查找该特定对象的索引,然后对其进行更新,如下所示:

case "COMPLETE_TASK":
    let index = state.todos.findIndex(todo => todo.id === action.id);
    let todos = [...state.todos];
    todos[index] = ...todos[index], completed: action.completed;
    return ...state, todos

检查这个 sn-p 你会更好地了解你正在做的错误:

let state = 
  a: 1,
  arr: [
    text:1, id:1, completed: true,
    text:2, id:2, completed: false
  ]


console.log('old values', JSON.stringify(state));

// updating the values

let newState = 
   ...state,
   arr: [completed: true]


console.log('new state = ', newState);

【讨论】:

【参考方案3】:

React 的重要设计原则之一是“不要改变状态”。如果要更改数组中的数据,则需要使用更改后的值创建一个新数组。

例如,我有一个状态数组。最初,我只是将构造函数中每个索引的值设置为 0。

this.state =            
       index:0,
        question: this.props.test[0].questions[0],
        results:[[0,0],[1,0],[2,0],[3,0],[4,0],[5,0]],
        complete: false         
;

稍后,我想更新数组中的一个值。但我没有在状态对象中更改它。在 ES6 中,我们可以使用扩展运算符。数组切片方法返回一个新数组,不会改变已有数组。

updateArray = (list, index,score) => 
   // updates the results array without mutating it
      return [
        ...list.slice(0, index),
        list[index][1] = score,
       ...list.slice(index + 1)
     ];
;

当我想更新数组中的一个项目时,我会调用 updateArray 并一次性设置状态:

this.setState(
        index:newIndex, 
        question:this.props.test[0].questions[newIndex],
        results:this.updateArray(this.state.results, index, score)
);

【讨论】:

以上是关于更新项目数组中的单个值 |反应还原的主要内容,如果未能解决你的问题,请参考以下文章

反应复选框组/单个复选框

如何使函数在映射数组中的单个项目而不是数组中的每个项目上执行?

更新数组中的单个项目,其中多个条件适用于任何数组项目

如何在反应中推送 JSON 对象数组中的项目?

是否有更简洁的 linq 方式来“联合”单个项目?

猫鼬填充数组中的单个项目