更新嵌套状态对象中的单个记录,react-redux

Update single record in nested state object, react-redux

我正在开发一个网格结构,用户可以在其中动态添加部分、子部分或项目。我在我的 redux 状态对象中管理这些东西。 UI 我的网格如下:

我想更新单行记录而不是重新加载整个网格。为此,每当用户更改我正在调用 update-row api 的行的任何单元格值时,如果成功,我将尝试使用以下代码在 reducer 中更新该值。

case UPDATE_ORDER_LINES_SUCCESS:
let stateData = state.get(`GridData`);
      const dataIndex = stateData.children.findIndex(
        (listing) => listing.id === action.row.id  // row id which is updated 
      );
      stateData[0].children[dataIndex] = action.row;
      let data = Object.assign(stateData, { children: stateData.children });
      state = state.set(`GridData`, [data]);

此代码对于第一级子记录(根据 json 对象)工作正常,但如果用户更新第 n 级子记录的值,则会出现问题。如何在我的 redux 状态下更新该行记录?

我当前的 redux 状态样本是:

    {
  "views": [
    {
      "id": "5e6b8961ba08180001a10bb6",
      "viewName": "house",
      "description": "house view",
      "name": "house",
      "children": [
        {
          "id": "5e6b8961ba08180001a10bb7",
          "viewName": "house",
          "sectionName": "Temporary",
          "sectionId": "SEC-02986",
          "description": "Temporary",
          "sequenceNumber": 4,
          "refrenceId": "SEC-02986",
          "children": [
            {
              "id": "5e590df71bbc71000118c109",
              "lineDescription": "AutoPickPack01",
              "lineAction": "Rent",
              "quantity": 5,
              "deliveryDate": "2020-02-29T06:00:00+11:00",
              "pickDate": "2020-02-28T06:00:00+11:00",
              "pickupDate": "2020-03-01T06:00:00+11:00",
              "prepDate": "2020-02-28T06:00:00+11:00",
              "returnDate": "2020-03-01T06:00:00+11:00",
              "shippingDate": "2020-02-29T06:00:00+11:00",
              "unitPrice": 7000,
              "children": [
                {
                  "id": "5e590df71bbc71000118c10a",
                  "orderId": "Ord-05788_1",
                  "lineNumber": "01a7b77c-792a-4edb-9b73-132440621968",
                  "purchaseOrderNumber": null,
                  "lineDescription": "29Janserial",
                  "lineAction": "Rent",
                  "quantity": 5,
                  "pricingMethod": "Fixed",
                  "displayUnit": "Days",
                  "unitPrice": 0,
                  "chargeAmount": 0,
                  "pickDate": "2020-02-17T06:00:00+11:00",
                  "prepDate": "2020-02-28T06:00:00+11:00",
                  "shippingDate": "2020-02-29T06:00:00+11:00",
                  "deliveryDate": "2020-02-29T06:00:00+11:00",
                  "pickupDate": "2020-03-01T06:00:00+11:00",
                  "returnDate": "2020-03-01T06:00:00+11:00",
                  "name": "29Janserial",
                  "description": "29Janserial",
                  "discountAmount": "",
                  "discountPrice": ""
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

在 reducer 中更新嵌套子行数据的最佳方法是什么?

您应该规范化您的状态,这会使树变平,并且实体通过 id 引用关联而不是直接嵌套。

例如

{
  "entities": {
    "orders": {
      "o1": { "id": "o1", "productIds": ["p1", "p2"] },
      "o2": { "id": "o2", "productIds": ["p2", "p3"] },
      "o3": { "id": "o2", "productIds": ["p3"] }
    },
    "products": {
      "p1": { "id": "p1", "orderIds": ["o1"] },
      "p2": { "id": "p1", "orderIds": ["o1", "o2"] },
      "p3": { "id": "p1", "orderIds": ["o2", "o3"] }
    },
    "views": {
      "v1": { "id": "v1", "childIds": ["v1.1", "v1.2"] },
      "v1.1": { "id": "v1.1", "parentId": "v1" },
      "v1.2": { "id": "v1.2", "parentId": "v1" }}
  },
  "ids": {
    "orders": ["o1", "o2", "o3"],
    "products": ["p1", "p2", "p3"],
    "views": ["v1", "v1.1", "v1.2"]
  }
}

找到正确模型并将原始数据转换为模型的前期工作较多,但您可以节省大量时间,而不必处理嵌套或影响多个数据区域的更新。

Redux docs on normalizing

A (de)normalization transformation tool

A reducer utility library to manage normalized state

由于 redux 不允许改变当前状态并 return 返回,因此很难修改嵌套的子级。尽管非常不鼓励 在 redux 中有这种嵌套结构,而应该将其规范化为 @bsapaka 回答。但是如果你仍然想更新嵌套 对象和 return 整个状态作为一个不可变的状态,immer 应该是你的朋友。 immerJS 在处理不可变 states.Although

方面非常受欢迎

在你的案例中安装 immer 和 redux-immer

yarn add immer redux-immer

在您的 reducers.js 文件中,所有减速器已使用 combineReducers

组合
import produce from 'immer';
import { combineReducers } from 'redux-immer';

// Replace your current combineReducers with
combineReducers(produce, { /* Object of all reducers */ });

在你当前的reducer文件中

import product from 'immer';

const findNestedChild = (arr, itemId) => (
  arr.reduce((a, item) => {
    if (a) return a;
    if (item.id === itemId) return item;
    if (item['children']) return findItemNested(item['children'], itemId)
  }, null)
);

case UPDATE_ORDER_LINES_SUCCESS:
  return produce(state, draftState => {
    const { row: newChild, row: { id }} = action;
    let child = findNestedChild(draftState.views, id);
    child = newChild;
  });