useReducer可以与状态数组一起使用吗?

时间:2019-07-30 21:47:11

标签: javascript reactjs react-hooks

我已经实现了一种相当简单的方法,可以通过以下操作将撤消添加到useReducer

export const stateMiddleware = reducerFunc => {
  return function(state = { history: [] }, action) {
    return {
      history:
        action.type === UNDO
          ? drop(state.history) // lodash drop
          : [reducerFunc(state.history[0], action), ...state.history],
    }
  }
}

const [state, dispatch] = useReducer(stateMiddleware(reducer), { history: [initialState] })

state变量包含整个历史记录,因此必须从第一个元素中提取当前状态。

我想知道useReducer是否会接受state参数的数组(并且也欢迎对我的方法发表评论-它避免使用似乎笨重的程序包执行类似的操作)< / p>

3 个答案:

答案 0 :(得分:1)

为了给您一些有关通用方法的反馈,我将引用Redux文档,因为它们提供了许多有关全局状态管理的最佳实践。如果您以后需要一个完整的存储,则它使切换更加容易。我在这里假设您不仅要使用useReducer来用于一个本地组件,而且要使用一个更全局的状态树(感谢@DrewReese对此发表评论)。如果只有一个组件左右,那么您的方法就可以了!

有关基本状态形状的Redux文档,以及在顶层(Link)上是否可以使用数组:

  

Redux状态通常具有一个纯Javascript对象作为状态树的顶部。 (当然也可以使用另一种类型的数据,例如单个数字,数组或特殊的数据结构,但是大多数库都假定顶级值是一个普通对象。)最常见的组织方式该顶级对象中的数据是将数据进一步分为子树,其中每个顶级键代表相关数据的某些“域”或“切片”。

所以基本上可以,但是其他库也可能会被优化并期望有一个对象,因为到目前为止这是最可能的选择。

如果您采用这种形状,请确保您的 whole 状态始终 需要撤消操作。因为这是有代价的(除了提到的第3方lib兼容性之外):

  • 直接在顶层再添加一层嵌套
  • 可扩展性和粒度(请参阅下一点)

如果您的应用程序变得非常庞大,并且您有数百个减速器,那该怎么办?每次调度您的操作时,都会在history数组中放置一个新的状态对象,这可能会造成混乱。 如果您不希望全局撤消,而是一个非常精细的撤消(在一个特定的reducer中,例如针对UI中的一个特定表单元素),该怎么办?然后,您必须区分全局撤消和特定撤消。可以只在每个感兴趣的减速器中以自己的方式处理的一个“ UNDO”动作又如何呢?

如果仍然符合您的要求,请尝试一下!

欢呼

答案 1 :(得分:1)

如果您真正要问的是是否可以使用纯数组初始化useReducer挂钩状态,那么可以。

Edit useReducer-with-array

答案 2 :(得分:0)

这是有可能的,但是正如ford04指出的那样,出于各种原因,它可能不是一个好主意。

下面的演示显示了如何使用普通数组作为state中的useReducer

const initialState = [];

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return [...state, state.length + 1];
    case 'decrement':
      return [...state.slice(0, -1)];
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = React.useReducer(reducer, initialState);
  return (
    <React.Fragment>
      <div>
        State: {state.join()}
      </div>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
    </React.Fragment>
  );
}

ReactDOM.render(<Counter />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>

<div id="app"></div>