在 React/Redux reducer 中,如何以不可变的方式更新嵌套数组中的字符串?

Posted

技术标签:

【中文标题】在 React/Redux reducer 中,如何以不可变的方式更新嵌套数组中的字符串?【英文标题】:In React/Redux reducer, how can I update a string in an nested array in an immutable way? 【发布时间】:2018-02-06 08:48:48 【问题描述】:

我的店铺是这样的,


  name: "john",
  foo: ,
    arr: [
      
        id:101,
        desc:'comment'
      ,
       
        id:101,
        desc:'comment2'
      
    ]

我的文本区域看起来像这样

<textarea
  id=arr.id //"101"
  name=`tesc:`
  value=this.props.store.desc
  onChange=this.props.onChng
/>

我的行动是

export const onChng = (desc) => (
  type: Constants.SET_DESC,
  payload: 
    desc
  
);

我的减速器

case Constants.SET_DESC:
  return update(state, 
    store: 
      streams: 
        desc:  $set: action.payload.desc 
       
    
);

只有当 arry 是一个对象时它才有效,我必须对数组的流进行更改,我很困惑如何更新到数组,以及如何从存储中获取正确的值。

【问题讨论】:

你想如何更新数组?修改数组中的一个元素或添加一个新元素、删除一个等? 我想添加一个新元素? id:101, here 旁边的 如何确定id? 我已将 textarea 更新为与流数组中的 id 相同 所以你想在每次输入textarea的时候,在streams数组中添加一个新对象,以textarea的id和value作为描述? 【参考方案1】:

从 redux 文档中获取的以下示例可能会帮助您在用例中如何更新数组中的项目。有关更多信息,您可以在此处阅读http://redux.js.org/docs/recipes/StructuringReducers.html

状态结构是这样的


  visibilityFilter: 'SHOW_ALL',
  todos: [
    
      text: 'Consider using Redux',
      completed: true,
    ,
    
      text: 'Keep all state in a single tree',
      completed: false
    
  ]

reducer 代码如下

function updateObject(oldObject, newValues) 
    // Encapsulate the idea of passing a new object as the first parameter
    // to Object.assign to ensure we correctly copy data instead of mutating
    return Object.assign(, oldObject, newValues);


function updateItemInArray(array, itemId, updateItemCallback) 
    const updatedItems = array.map(item => 
        if(item.id !== itemId) 
            // Since we only want to update one item, preserve all others as they are now
            return item;
        

        // Use the provided callback to create an updated item
        const updatedItem = updateItemCallback(item);
        return updatedItem;
    );

    return updatedItems;


function appReducer(state = initialState, action) 
    switch(action.type) 
        case 'EDIT_TODO' : 
            const newTodos = updateItemInArray(state.todos, action.id, todo => 
                return updateObject(todo, text : action.text);
            );

            return updateObject(state, todos : newTodos);
         
        default : return state;
    

【讨论】:

我希望在我要求的结构中做到这一点。意思是你如何访问商店中的数组 state.todos 是数组 不幸的是我不明白.. 但感谢您的尝试【参考方案2】:

如果您必须更新商店中数组中的元素,则必须复制该数组并克隆匹配的元素以应用您的更改。

因此,在第一步中,您的操作应包含已克隆(和更改)的对象或对象的 id 和要更改的属性。

这是一个粗略的例子:

export class MyActions 
    static readonly UPDATE_ITEM = 'My.Action.UPDATE_ITEM';

    static updateItem(id: string, changedValues: any) 
        return  type: MyActions.UPDATE_ITEM, payload:  id, changedValues  ;
    


export const myReducer: Reducer<IAppState> = (state: IAppState = initialState, action: AnyAction): IAppState => 
    switch (action.type) 
        case MyActions.UPDATE_ITEM:
            return  ...state, items: merge(state.items, action.payload) ;

        default:
            return state;
    


const merge = (array, change) => 
    // check if an item with the id already exists
    const index = array.findIndex(item => item.id === change.id);
    // copy the source array
    array = [...array];

    if(index >= 0) 
        // clone and change the existing item
        const existingItem = array[index];
        array[index] =  ...existingItem, ...change.changedValues ;
     else 
        // add a new item to the array
        array.push =  id: change.id, ...change.changedValues ;
    

    return array;

【讨论】:

array 在您的示例中会是什么样子? array 包含您的对象。例如。 id: string, desc: string 。如果您的对象还包含一组其他对象,则您必须将上述逻辑再嵌套一层,并且您的 changedValues 对象也将包含一个 id 属性。如果这是您的情况并且您不知道如何完成此操作,请给我提示,我会更新我的答案。【参考方案3】:

要更新数组,我会使用 immutability helper 并执行类似的操作 - 对您的减速器

 let store = "state" : 
"data": [
    "subset": [
        "id": 1
    , 
        "id": 2
    ]
, 
    "subset": [
        "id": 10
    , 
        "id": 11
    , 
        "id": 12
    ]
]


case Constants.SET_DESC:
  return update(store, 
  "state" : 
  "data": 
      [action.indexToUpdate]: 
        "subset": 
             $set: action.payload.desc
        
      
  
 
  )
  );

【讨论】:

这就是我想要实现它的方式。我已将 immutable-helper 下载到我的项目中,但它不起作用 - 找不到 indexToUpdate

以上是关于在 React/Redux reducer 中,如何以不可变的方式更新嵌套数组中的字符串?的主要内容,如果未能解决你的问题,请参考以下文章

React / Redux:在reducer中更新对象的正确方法

React Redux:在 Reduce 状态下使用不可变

React + Redux:reducer 不会重新加载组件

在初始化期间,React / Redux reducer返回undefined

react redux Reduc-saga实现 take put takeEvery createSagaMiddleware等

我可以在没有 action creators 和 reducers 的情况下在 React 和 Redux 中发送 AJAX 调用吗?