我有一个 redux 存储。若要更改存储中的数据,典型的方法是创建一个操作、actionCreator、一个化简器,然后调度该操作。 对于一个中小型应用程序来说,在这么多地方进行更改以反映此类更改似乎有些矫枉过正。所以我创建了一个通用的减速器,看起来像这样:
// here state is a copy of actual state, so I can mutate it directly
const reducer = ( state, action) => {
if(action.type == 'SETTER'){
try{
return assign(state, action.data.target, action.data.value )
}
catch(err){
console.log('WARNING: the key wasn't valid', err)
}
}
return state;
}
这个assign
方法如下:
const assign = (obj, prop, value) => {
if (typeof prop === "string")
prop = prop.split(".");
if (prop.length > 1) {
var e = prop.shift();
assign(obj[e] , prop, value);
} else
obj[prop[0]] = value;
return obj
}
然后我有一个通用的操作调度程序和一个容器组件,它允许我做这样的事情:
containerComponent.set( 'user.name', 'Roy' )
containerComponent.set( 'order.receiver.address', 'N/A')
在容器组件上调用set
时触发的操作如下所示:
{
type : 'SETTER',
data : {
target : 'user.name',
value : 'Roy'
}
}
如您所见,这个通用化简器允许我再也不写化简器了,但是每当状态发生变化时,我仍然会调度一个动作,因此不违反 redux 的任何核心原则。
这种方法是否有任何小/大缺点,特别是在性能方面?而且,您认为这种方法在哪里有用。
正如您正确指出的那样,Redux 要求您在应用程序中发生某些事情的点和存储实际更新以反映此事件的点之间实现多层间接寻址。
这是设计使然。
全局状态通常会产生一个问题,即它可以从应用程序中的任何位置任意更改,而没有简单的方法来理解如何或为什么。是的,Redux存储实际上是全局状态。
通过将发生了什么(由操作表示并由操作的有效负载描述)的问题与如何影响全局状态(由化简器定义)分开,Redux 在一定程度上消除了这个问题。不允许对全局状态进行任意更改,只能进行某些非常具体的更改组合,这些更改由定义明确的事件触发,在最好的情况下,这些事件附带足够的语义信息,以便能够将它们追溯到其来源。
通过创建一对通用操作和化简器来破坏 Redux 的这个核心思想,你失去了 Redux 的核心优势之一,在你的组件和存储之间留下了一组间接和抽象,这些和抽象并没有真正给你带来任何显着的好处。
人们普遍认为,不创造价值的代码最好删除代码。在我看来,最好不要使用 Redux,而只是使用组件状态,而不是使用残缺的 Redux 实现。
创建Redux的Dan Abramov关于这个主题的有趣读物:你可能不需要Redux。
Timo的回答很好地解释了为什么你的实现违背了Redux的许多原则。我只想补充一点,你可能会觉得Mobx很有趣。我认为它更类似于你想要获得的那种状态管理。
这个函数(由reducer调用的赋值)不是根据Redux规则,这不是不可变的"纯"函数,因为突变状态。
测试:
const assign = (obj, prop, value) => {
if (typeof prop === "string")
prop = prop.split(".");
if (prop.length > 1) {
var e = prop.shift();
assign(obj[e], prop, value);
} else
obj[prop[0]] = value;
return obj
}
const reducer = (state, action) => {
if (action.type == 'SETTER') {
try {
return assign(state, action.data.target, action.data.value)
}
catch (err) {
console.log('WARNING: the key wasn't valid', err)
}
}
return state;
}
const testReducer = () => {
const user = {
id: 0,
name: ''
};
const action = {
type: 'SETTER',
data: {
target: 'name',
value: 'Roy'
}
};
console.log('user before: ', user);
reducer(user, action);
console.log('user after: ', user);
};
testReducer();
测试结果:
user before: { id: 0, name: '' }
user after: { id: 0, name: 'Roy' }
最简单的解决方法:
const assign = (obj, prop, value) => {
var tempObj = Object.assign({}, obj);
if (typeof prop === "string")
prop = prop.split(".");
if (prop.length > 1) {
var e = prop.shift();
assign(tempObj[e], prop, value);
} else
tempObj[prop[0]] = value;
return tempObj
}
编辑
修复而不将状态对象的值复制到临时目标对象:
const assign = (obj, prop, value) => {
if (typeof prop === "string")
prop = prop.split(".");
if (prop.length > 1) {
var e = prop.shift();
assign(obj[e], prop, value);
} else {
return {
...obj,
[prop[0]]: value
};
}
}
Reducer 现在是简单的函数,可以轻松重用
const getData = (state, action) => {
return {...state, data: state.data.concat(action.payload)};
};
const removeLast = (state) => {
return {...state, data: state.data.filter(x=>x !== state.data[state.data.length-1])};
}
操作类型和化简器函数现在在数组中声明
const actions = [
{type: 'GET_DATA', reducer: getData},
{type: 'REMOVE_LAST', reducer: removeLast},
{type: 'REMOVE_FIRST', reducer: removeFirst},
{type: 'REMOVE_ALL', reducer: removeAll},
{type: 'REMOVE_BY_INDEX', reducer: removeByIndex}
];
减速器的初始状态
const initialState = {
data: []
}
actionGenerators 使用 Symbol 创建一个唯一的 Id,并将该 Id 分配给动作和化简器函数。
const actionGenerators = (actions) => {
return actions.reduce((a,c)=>{
const id = Symbol(c.type);
a.actions = {...a.actions, [c.type]: id};
a.reducer = a.reducer ? a.reducer.concat({id, reducer: c.reducer}) : [{id, reducer: c.reducer}];
return a;
},{});
}
reducerGenerators是一个通用的减速器创建者。
const reducerGenerators = (initialState, reducer) => {
return (state = initialState, action) => {
const found = reducer.find(x=>x.id === action.type);
return found ? found.reducer(state, action) : state;
}
}
用法
const actionsReducerCreator = actionGenerators(actions);
const store = createStore(reducerGenerators(initialState, actionsReducerCreator.reducer));
const {GET_DATA} = actionsReducerCreator.actions;
store.dispatch({type: GET_DATA});
我已经在我的github上的待办事项应用程序中实现了这一点 冗余-减速器-生成器