我为状态创建了一个通用的reducer,什么时候应该使用它,什么时候不应该使用它

时间:2018-01-17 19:17:12

标签: javascript reactjs react-native redux react-redux

我有一个redux商店。要更改商店中的数据,通常的方法是创建操作,actionCreator,reducer,然后分派操作。 对于中小型应用程序,在很多地方进行更改以反映此类更改看起来有点过分。所以我创建了一个通用的reducer,看起来像这样:

// 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')

在containerComponent上调用set时触发的动作如下所示:

{
  type : 'SETTER',
  data : {
    target : 'user.name',
    value : 'Roy'
  }
}

正如你所看到的,这个通用的reducer让我永远不会再写一个reducer,但是我仍然会在状态发生变化时调度一个动作,所以不会违反redux的任何核心原则。

这种方法是否存在轻微/重大缺陷,特别是在性能方面?而且,你在哪里发现这种方法是有用的。

4 个答案:

答案 0 :(得分:6)

正如您所指出的那样,Redux要求您在应用程序中发生某些事情的位置与实际更新存储以反映此事件的点之间实现多层间接。

这是设计的。

全局状态通常会产生一个问题,即它可以在应用程序的任何位置任意更改,而无需简单的方法来了解方法或原因。是的,Redux商店实际上是全球性的。

通过如何影响全局状态来分离发生的事情(由动作表示并由动作的有效负载描述)的问题由reducer定义),Redux在一定程度上消除了这个问题。不是允许对全局状态进行任意更改,而是可以进行某些非常具体的更改组合,由明确定义的事件触发,在最好的情况下,附加足够的语义信息以使其能够追溯到其原点。

通过创建一对通用动作和减速器来破坏Redux的这一核心理念,您将失去Redux的核心优势之一,并且在您的组件和不包含的商店之间留下一组间接和抽象。 39;真的给你带来任何重大的好处。

众所周知,没有创造价值的代码是最好的代码删除。在我看来,你可能会更好地不使用Redux而只是使用组件状态而不是使用Redux的残缺实现。

Dan Abramov撰写了一篇关于此主题的有趣读物,他创建了Redux:You might not need Redux

答案 1 :(得分:0)

Timo的回答很好地解释了为什么你的实现违反了Redux的许多原则。我想补充一点,你可能会发现Mobx有趣。我认为它更类似于你想要获得的那种状态管理。

答案 2 :(得分:0)

这个函数(由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
    };
  }
}

答案 3 :(得分:0)

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])};
}

动作类型和reducer函数现在在数组中声明

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分配给action和reducer函数。

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是通用的reducer创建者。

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上的todo应用程序中实现了此功能 Redux-Reducer-Generator