在 Redux 中合并状态对象

Merge state objects in Redux

我的状态由用户选择的设置组成,用于查看应用程序中的图表。每次用户登录时,我都会从数据库中获取他们各自的设置。我还在我的减速器中定义了一个初始状态。将我从数据库收到的设置合并到我的 redux 状态的最佳方法是什么?我看过使用扩展运算符、Immer 等的示例,但我不太了解如何在此处“合并”两个状态对象。

这里的一个复杂问题是数据库中可能不存在新设置,因此需要默认设置。因此,我需要将初始状态与我从数据库中获得的设置合并。

初始状态

const initialState = {
    "1-0": {
        "view": "Snapshot",
        "bucket": "3_7",
        "group" : "Ratings",
        "metric": "Total Inquiry Count",
        "unit"  : null,
        "min"   : 0,
        "max"   : 1000,
        "Ratings": ["A-Index", "A-Non-Index", "BBB-Index", "BBB-Non-Index"],
        "Sectors": ["Financials-Index", "Energy-Index", "Information Technology-Index"],
        "Clusters": ["0-Index", "2-Index", "2-Non-Index", "6-Index"],
        "Signals": ["0-Index", "0-Non-Index", "1-Index", "1-Non-Index", "8-Index", "8-Non-Index", "9-Index", "9-Non-Index"]
    },
    "1-1": {
        "view": "Snapshot",
        "bucket": "3_7",
        "group" : "Sectors",
        "metric": "Total Inquiry Count",
        "unit"  : null,
        "min"   : 0,
        "max"   : 1000,
        "Ratings": ["A-Index", "A-Non-Index", "BBB-Index", "BBB-Non-Index"],
        "Sectors": ["Financials-Index", "Energy-Index", "Information Technology-Index"],
        "Clusters": ["0-Index", "2-Index", "2-Non-Index", "6-Index"],
        "Signals": ["0-Index", "0-Non-Index", "1-Index", "1-Non-Index", "8-Index", "8-Non-Index", "9-Index", "9-Non-Index"]
    },
    "1-2": {
        "view": "Snapshot",
        "bucket": "3_7",
        "group" : "Clusters",
        "metric": "Total Inquiry Count",
        "unit"  : null,
        "min"   : 0,
        "max"   : 1000,
        "Ratings": ["A-Index", "A-Non-Index", "BBB-Index", "BBB-Non-Index"],
        "Sectors": ["Financials-Index", "Energy-Index", "Information Technology-Index"],
        "Clusters": ["0-Index", "2-Index", "2-Non-Index", "6-Index"],
        "Signals": ["0-Index", "0-Non-Index", "1-Index", "1-Non-Index", "8-Index", "8-Non-Index", "9-Index", "9-Non-Index"]
    },
    "1-3": {
        "view": "Snapshot",
        "bucket": "3_7",
        "group" : "Signals",
        "metric": "Total Inquiry Count",
        "unit"  : null,
        "min"   : 0,
        "max"   : 1000,
        "Ratings": ["A-Index", "A-Non-Index", "BBB-Index", "BBB-Non-Index"],
        "Sectors": ["Financials-Index", "Energy-Index", "Information Technology-Index"],
        "Clusters": ["0-Index", "2-Index", "2-Non-Index", "6-Index"],
        "Signals": ["0-Index", "0-Non-Index", "1-Index", "1-Non-Index", "8-Index", "8-Non-Index", "9-Index", "9-Non-Index"]
    },
    "Total Inquiry Count" : {
        "unit": null,
        "min" : 0,
        "max" : 1000
    },
    "Total Inquiry Volume" : {
        "unit": "$",
        "min" : 0,
        "max" : 1000000000
    },
    "Quoted Inquiry Count" : {
        "unit": null,
        "min" : 0,
        "max" : 1000
    },
    "Quoted Inquiry Volume" : {
        "unit": "$",
        "min" : 0,
        "max" : 1000000000
    },
    "Transaction Count" : {
        "unit": "$",
        "min" : 0,
        "max" : 50
    },
    "Transaction Volume Gross" : {
        "unit": "$",
        "min" : 0,
        "max" : 5000000
    },
    "Transaction Volume Net" : {
        "unit": "$",
        "min" : -2000000,
        "max" : 2000000
    }
}

从数据库加载状态

{
  "1-0": {
    "view": "Series",
    "bucket": "3_7",
    "group": "Ratings",
    "metric": "Total Inquiry Count",
    "unit": null,
    "min": 0,
    "max": 1000,
    "Ratings": [
      "BBB-Index",
      "A-Non-Index",
      "AA-Non-Index",
      "AA-Index",
      "A-Index",
      "AAA-Index",
      "AAA-Non-Index"
    ],
    "Sectors": [
      "Financials-Index",
      "Energy-Index",
      "Information Technology-Index"
    ],
    "Clusters": [
      "0-Index",
      "2-Index",
      "2-Non-Index",
      "6-Index"
    ],
    "Signals": [
      "0-Index",
      "0-Non-Index",
      "1-Index",
      "1-Non-Index",
      "8-Index",
      "8-Non-Index",
      "9-Index",
      "9-Non-Index"
    ]
  },
  "1-1": {
    "view": "Series",
    "bucket": "3_7",
    "group": "Ratings",
    "metric": "Total Inquiry Volume",
    "unit": "$",
    "min": 0,
    "max": 1000000000,
    "Ratings": [
      "BB-Index",
      "BB-Non-Index"
    ],
    "Sectors": [
      "Financials-Index",
      "Energy-Index",
      "Information Technology-Index"
    ],
    "Clusters": [
      "0-Index",
      "2-Index",
      "2-Non-Index",
      "6-Index"
    ],
    "Signals": [
      "0-Index",
      "0-Non-Index",
      "1-Index",
      "1-Non-Index",
      "8-Index",
      "8-Non-Index",
      "9-Index",
      "9-Non-Index"
    ]
  },
  "1-2": {
    "view": "Snapshot",
    "bucket": "3_7",
    "group": "Clusters",
    "metric": "Total Inquiry Count",
    "unit": null,
    "min": 0,
    "max": 1000,
    "Ratings": [
      "A-Index",
      "A-Non-Index",
      "BBB-Index",
      "BBB-Non-Index"
    ],
    "Sectors": [
      "Financials-Index",
      "Energy-Index",
      "Information Technology-Index"
    ],
    "Clusters": [
      "0-Index",
      "2-Index",
      "2-Non-Index",
      "6-Index"
    ],
    "Signals": [
      "0-Index",
      "0-Non-Index",
      "1-Index",
      "1-Non-Index",
      "8-Index",
      "8-Non-Index",
      "9-Index",
      "9-Non-Index"
    ]
  },
  "1-3": {
    "view": "Snapshot",
    "bucket": "3_7",
    "group": "Signals",
    "metric": "Total Inquiry Count",
    "unit": null,
    "min": 0,
    "max": 1000,
    "Ratings": [
      "A-Index",
      "A-Non-Index",
      "BBB-Index",
      "BBB-Non-Index"
    ],
    "Sectors": [
      "Financials-Index",
      "Energy-Index",
      "Information Technology-Index"
    ],
    "Clusters": [
      "0-Index",
      "2-Index",
      "2-Non-Index",
      "6-Index"
    ],
    "Signals": [
      "0-Index",
      "0-Non-Index",
      "1-Index",
      "1-Non-Index",
      "8-Index",
      "8-Non-Index",
      "9-Index",
      "9-Non-Index"
    ]
  },
  "Total Inquiry Count": {
    "unit": null,
    "min": 0,
    "max": 1000
  },
  "Total Inquiry Volume": {
    "unit": "$",
    "min": 0,
    "max": 1000000000
  },
  "Transaction Count": {
    "unit": "$",
    "min": 0,
    "max": 50
  },
  "Transaction Volume Gross": {
    "unit": "$",
    "min": 0,
    "max": 5000000
  },
  "Transaction Volume Net": {
    "unit": "$",
    "min": -1000000,
    "max": 1000000
  }
}

当前不正确的实现

export function panels(state=initialState, action={}) {
    switch(action.type) {
case SET_VIEW:
    const newViewState = {
        ...state,
        [action.key] : {
            ...state[action.key],
            view: action.payload
        }
    };
    return newViewState;
case SET_BUCKET:
    const newBucketState = {
        ...state,
        [action.key] : {
            ...state[action.key],
            bucket: action.payload
        }
    };
    return newBucketState;
case SET_GROUP:
    const newGroupState = {
        ...state,
        [action.key] : {
            ...state[action.key],
            group: action.payload
        }
    };
    return newGroupState;
case SET_METRIC:
    const newMetricState = {
        ...state,
        [action.key] : {
            ...state[action.key],
            metric: action.payload,
            unit: state[action.payload]['unit'],
            min: state[action.payload]['min'],
            max: state[action.payload]['max']
        }
    };
    return newMetricState;
 case SETTINGS_LOADED:
    if(!action.payload || Object.keys(action.payload).length === 0) {
      return initialState;
    }
    return action.payload;
 default:
    return state;
}

期望输出

{
"1-0": {
    "view": "Series",
    "bucket": "3_7",
    "group": "Ratings",
    "metric": "Total Inquiry Count",
    "unit": null,
    "min": 0,
    "max": 1000,
    "Ratings": [
      "BBB-Index",
      "A-Non-Index",
      "AA-Non-Index",
      "AA-Index",
      "A-Index",
      "AAA-Index",
      "AAA-Non-Index"
    ],
    "Sectors": [
      "Financials-Index",
      "Energy-Index",
      "Information Technology-Index"
    ],
    "Clusters": [
      "0-Index",
      "2-Index",
      "2-Non-Index",
      "6-Index"
    ],
    "Signals": [
      "0-Index",
      "0-Non-Index",
      "1-Index",
      "1-Non-Index",
      "8-Index",
      "8-Non-Index",
      "9-Index",
      "9-Non-Index"
    ]
  },
  "1-1": {
    "view": "Series",
    "bucket": "3_7",
    "group": "Ratings",
    "metric": "Total Inquiry Volume",
    "unit": "$",
    "min": 0,
    "max": 1000000000,
    "Ratings": [
      "BB-Index",
      "BB-Non-Index"
    ],
    "Sectors": [
      "Financials-Index",
      "Energy-Index",
      "Information Technology-Index"
    ],
    "Clusters": [
      "0-Index",
      "2-Index",
      "2-Non-Index",
      "6-Index"
    ],
    "Signals": [
      "0-Index",
      "0-Non-Index",
      "1-Index",
      "1-Non-Index",
      "8-Index",
      "8-Non-Index",
      "9-Index",
      "9-Non-Index"
    ]
  },
  "1-2": {
    "view": "Snapshot",
    "bucket": "3_7",
    "group": "Clusters",
    "metric": "Total Inquiry Count",
    "unit": null,
    "min": 0,
    "max": 1000,
    "Ratings": [
      "A-Index",
      "A-Non-Index",
      "BBB-Index",
      "BBB-Non-Index"
    ],
    "Sectors": [
      "Financials-Index",
      "Energy-Index",
      "Information Technology-Index"
    ],
    "Clusters": [
      "0-Index",
      "2-Index",
      "2-Non-Index",
      "6-Index"
    ],
    "Signals": [
      "0-Index",
      "0-Non-Index",
      "1-Index",
      "1-Non-Index",
      "8-Index",
      "8-Non-Index",
      "9-Index",
      "9-Non-Index"
    ]
  },
  "1-3": {
    "view": "Snapshot",
    "bucket": "3_7",
    "group": "Signals",
    "metric": "Total Inquiry Count",
    "unit": null,
    "min": 0,
    "max": 1000,
    "Ratings": [
      "A-Index",
      "A-Non-Index",
      "BBB-Index",
      "BBB-Non-Index"
    ],
    "Sectors": [
      "Financials-Index",
      "Energy-Index",
      "Information Technology-Index"
    ],
    "Clusters": [
      "0-Index",
      "2-Index",
      "2-Non-Index",
      "6-Index"
    ],
    "Signals": [
      "0-Index",
      "0-Non-Index",
      "1-Index",
      "1-Non-Index",
      "8-Index",
      "8-Non-Index",
      "9-Index",
      "9-Non-Index"
    ]
  },
    "Total Inquiry Count" : {
        "unit": null,
        "min" : 0,
        "max" : 1000
    },
    "Total Inquiry Volume" : {
        "unit": "$",
        "min" : 0,
        "max" : 1000000000
    },
    "Quoted Inquiry Count" : {
        "unit": null,
        "min" : 0,
        "max" : 1000
    },
    "Quoted Inquiry Volume" : {
        "unit": "$",
        "min" : 0,
        "max" : 1000000000
    },
    "Transaction Count" : {
        "unit": "$",
        "min" : 0,
        "max" : 50
    },
    "Transaction Volume Gross" : {
        "unit": "$",
        "min" : 0,
        "max" : 5000000
    },
    "Transaction Volume Net" : {
        "unit": "$",
        "min" : -1000000,
        "max" : 1000000
    }
}

您可以使用以下方法合并初始状态和数据库中的设置:-

function mergeUserSetting (initialState, stateFromDatabase) {
  const result = {}
  for (let [key, value] of Object.entries(initialState)) {
    const type = Object.prototype.toString.call(value).slice(8, -1)
    if (type === 'Object') {
      result[key] = stateFromDatabase[key] ? mergeUserSetting(value, stateFromDatabase[key]) : value
    } else {
      result[key] = stateFromDatabase[key] ? stateFromDatabase[key] : value
    }
  }
  return result
}

export function panels (state = initialState, action = {}) {
  switch (action.type) {
    case SETTINGS_LOADED:
      if (!action.payload || Object.keys(action.payload).length === 0) {
        return state;
      }
      return mergeUserSetting(state, action.payload);
    default:
      return state;
  }
}