在 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;
}
}
我的状态由用户选择的设置组成,用于查看应用程序中的图表。每次用户登录时,我都会从数据库中获取他们各自的设置。我还在我的减速器中定义了一个初始状态。将我从数据库收到的设置合并到我的 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;
}
}