javascript - 带有自定义函数的 Redux、mutation 和 deepMerge
问题描述
在这里使用 Redux。我想使用胖动作,即在我的动作中包含所有可能的逻辑——以及 thunk——,并使减速器尽可能小。
为了实现这一点,我只有一个减速器,例如:
const types = {
ADD_ARTICLE: 'ADD_ARTICLE',
REMOVE_ARTICLE: 'REMOVE_ARTICLE'
};
const initialState = {
articles: {
byKey: {},
byId: [],
},
};
const ArticlesReducer = (state = initialState, action) => {
return Object.hasOwnProperty.call(types, action.type)
? Object.assign({}, state, action.payload)
: state;
};
现在一个动作可以像
export const addArticle = () => ({
type: types.ADD_ARTICLE,
payload: {
byKey: {
[1]: {
id: 1,
title: 'My first article',
},
},
},
});
并且 reducer 会自动更新Articles.byKey
. 但现在我添加另一篇文章:
export const addArticle = () => ({
type: types.ADD_ARTICLE,
payload: {
byKey: {
[2]: {
id: 2,
title: 'My second article',
},
},
},
});
而reducer,就像使用Object.assign一样,只是删除了第一篇文章。所以我需要做一个深度合并。为此,我创建了一个自定义合并函数:
const isObject = (item) =>
item && typeof item === "object" && !Array.isArray(item);
const isArray = (item) =>
item && typeof item === "object" && !!Array.isArray(item);
// Deep merge Objects and Arrays
// If we want to remove elements, assign them as `null` or `undefined`
const mergeDeep = (
target,
sources,
replaceEmptyArrays = false,
replaceEmptyObjects
) => {
if (!sources.length) return target;
const source = sources.shift();
if (isObject(target) && isObject(source)) {
for (const key in source) {
// Object case
if (isObject(source[key])) {
// If is an object, deep merge it
if (!target[key]) {
// If there is no current key in the target, create it and skip
Object.assign(target, { [key]: source[key] });
} else if (!!replaceEmptyObjects) {
// If there is key, and we want to replace it, do Object.assign
Object.assign(target, { [key]: source[key] });
} else {
// If there is key, and we want do deepMerge it, do recursive mergeDeep
mergeDeep2(target[key], [source[key]], { replaceEmptyArrays, replaceEmptyObjects });
}
}
// Array case
if (isArray(source[key])) {
if (replaceEmptyArrays) {
// If is an array, and we DO NOT want to deep merge it with `!!replaceEmptyArrays` option—, we object.assign it, replacing it if empty
Object.assign(target, { [key]: source[key] });
} else {
// If is an array, and we want to deep merge it —by default— with `!replaceEmptyArrays` option—, we merge it and remove duplicates with a Set
Object.assign(target, {
[key]: Array.from(new Set([...target[key], ...source[key]])),
});
}
}
// Neither Object nor Array casess
if (!isObject(source[key]) && !isArray(source[key])) {
// Simply replace it with Object.assign
Object.assign(target, { [key]: source[key] });
}
}
}
return Object.assign({}, mergeDeep2(target, [...sources], { replaceEmptyArrays, replaceEmptyObjects }));
};
它有效:
const target = {
a: 1,
b: {
c: 1,
d: {
e: {
f: 1,
g: 2,
},
},
},
};
const source = {
a: 1,
b: {
c: 1,
d: {
e: {
h: 3,
},
},
j: {
k: 1,
},
},
};
const expectedResult = {
a: 1,
b: {
c: 1,
d: {
e: {
f: 1,
g: 2,
h: 3,
},
},
j: {
k: 1,
},
},
};
console.log("=======");
console.log("expectedResult:");
console.log(JSON.stringify(expectedResult, null, 4));
console.log("--------------------------------------------------------");
console.log("mergeDeep(target, [source]):");
console.log(JSON.stringify(mergeDeep(target, [source]), null, 4));
console.log("=======");
但是现在,回到 Redux,当我在 reducer 中使用它时:
const ArticlesReducer = (state = initialState, action) => {
return Object.hasOwnProperty.call(types, action.type)
? mergeDeep(state, [action.payload])
: state;
};
Redux 不会更新 UI。这意味着存在突变:但是,如果我的合并函数返回带有 Object.assign 的合并对象,那怎么可能呢?
[…]
return Object.assign({}, mergeDeep2(target, [...sources], { replaceEmptyArrays, replaceEmptyObjects }));
我可以将标志设置replaceEmptyObjects
为true
,redux 将再次工作:
const ArticlesReducer = (state = initialState, action) => {
return Object.hasOwnProperty.call(types, action.type)
? mergeDeep(state, [action.payload], {
replaceEmptyObjects: true,
})
: state;
};
但我根本不会是一个深度合并。
我可以使用 lodash 合并,它可以深度合并——或某种——;但我想控制我的对象合并的方式,并理解为什么会发生这种情况。
解决方案
推荐阅读
- c# - 如何在 MVC 5 中使用 jQuery AJAX 和 setInterval 加载 div?
- css - 元素之间的列数高度问题
- java - 使用 Java 流以不可变的方式更改数据
- java - 如何为类中的 ArrayList 赋值?
- php - laravel 中的动态类别级别
- android - 如何搜索从数据库中输入edittext值
- tmux - Byobu-tmux vi-copy 在系统升级后不起作用(KDE Neon)
- java - log4j 指向错误的反斜杠日志路径
- regex - 错误:Big Query REGEX_EXTRACT 无法解析正则表达式:无效的 perl 运算符:(?<
- java - 如何在我的 Eclipse IDE 中删除此错误标志?