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

6

我的商店看起来像这样:

{
  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
  }
});

我的 reducer

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

只有当 arry 是一个对象时,它才有效,我不得不对流进行更改以转换为数组,并且我很困惑如何更新为一个数组,还有如何从存储中获取正确的值。


1
你想如何更新数组?修改数组的元素还是添加新元素,移除元素等? - Andrew Li
我想在 { id:101, here } 旁边添加一个新元素? - Zena Mesfin
1
你如何确定这个ID? - Andrew Li
我已经更新了文本区域,使其具有与流数组内部相同的ID。 - Zena Mesfin
1
你想每次在文本区域输入时,将新对象添加到“streams”数组中,并使用文本区域的ID和值作为描述吗? - Andrew Li
是的,但我愿意接受其他更好的实现方式。 - Zena Mesfin
3个回答

2
以下示例摘自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;
    }
}

我希望按照我提出的结构来完成它。也就是说,如何在存储中访问数组。 - Zena Mesfin
state.todos 是一个数组。 - Arun Redhu
很遗憾我不理解,但感谢您的尝试。 - Zena Mesfin

1
如果您需要更新存储中数组中的元素,则必须复制该数组并克隆匹配的元素以应用更改。
因此,在第一步中,您的操作应包含已克隆(并更改)的对象或要更改的对象的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会是什么样子? - Zena Mesfin
array 包含您的对象。例如 { id: string, desc: string }。如果您的对象还包含进一步对象的数组,则必须将上述逻辑嵌套一个级别,并且您的 changedValues 对象也将包含一个 id 属性。如果这是您的情况,而您不知道如何实现,请给我提示,我会更新我的答案。 - Oliver

0
要更新一个数组,我会使用不可变助手并在你的reducer中做类似这样的事情。
 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。 - Zena Mesfin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接