useReducer中第三个参数的目的是什么?

时间:2019-11-25 22:58:56

标签: javascript reactjs react-hooks use-reducer

来自docs

  

[init,3d参数]使您可以提取用于在化简器外部计算初始状态的逻辑。这对于以后响应某个操作重置状态也很方便。

和代码:

function init(initialCount) {
  return { count: initialCount };
}

function reducer(state, action) {
  switch (action.type) {
    ...
    case 'reset':
      return init(action.payload);
    ...
  }
}

function Counter({initialCount}) {
  const [state, dispatch] = useReducer(reducer, initialCount, init);
  ...
}

为什么我要重用常数initialState来做到这一点?

const initialState = {
  count: 5,
};

function reducer(state, action) {
  switch (action.type) {
    ...
    case 'reset':
      return initialState;
    ...
  }
}

function Counter({initialCount}) {
  const [state, dispatch] = useReducer(reducer, initialState);
  ...
}

对我来说不那么冗长。

3 个答案:

答案 0 :(得分:9)

编辑2020年7月:React documentation现在对这个名为lazy initializer的参数有更好的解释。以其他方式使用此功能可能会由于未记录的效果而导致更改更改。以下答案仍然有效。


据我所知,init函数作为第三个arg是initialState的变换器。

这意味着initialState不会被用作初始状态,而将作为init函数的arg。这一个的返回将是真实的initialState。避免在useReducer初始化行期间出现巨大的参数。

/* Here is the magic. The `initialState` pass to 
 * `useReducer` as second argument will be hook
 * here to init the real `initialState` as return
 * of this function
 */
const countInitializer = initialState => {
  return {
    count: initialState,
    otherProp: 0
  };
};

const countReducer = state => state; // Dummy reducer

const App = () => {
  const [countState /*, countDispatch */] =
    React.useReducer(countReducer, 2, countInitializer);

  // Note the `countState` will be initialized state direct on first render
  return JSON.stringify(countState, null, 2);
}

ReactDOM.render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>

答案 1 :(得分:4)

我的理解是,惰性初始化是为特殊情况设计的,这种特殊情况下,初始化状态的代码需要占用大量内存或CPU,因此开发人员希望将状态数据的范围保留在组件内部。

例如,如果您要设计一个PhotoPane组件,该组件可容纳用于编辑的高清照片。

const PhotoPane = (props) => {
    const initialPixelData = loadPhoto(props.photoID);
    const [pixelData, dispatch] = useReducer(reducerFunc, initialPixelData);
    ...
}

由于重复调用loadPhoto(),因此上述代码存在严重的性能问题。如果不想在每次渲染组件时都再次加载照片,则直观的反应是将loadPhoto(props.photoID)从组件中移出。但这会引起另一个问题。您将必须将所有照片加载到Context或其他位置的内存中,这肯定会创建一个内存猪。

因此这是我们引入延迟初始化的时候了。请查看下面的代码。

const PhotoPane = (props) => {
    const init = (photoID) => loadPhoto(photoID);
    const [pixelData, dispatch] = useReducer(reducerFunc, props.photoID, init);
    ...
}

init()函数仅在第一次调用useReducer时执行一次。

实际上,useEffect()挂钩可以实现类似的结果。但是延迟初始化仍然是最直接的解决方案。

答案 2 :(得分:3)

useReducer接受可选的第三个参数initialAction。如果提供,则在初始渲染期间应用初始动作。

例如:

function Counter({ initialCount }) {
  const [state, dispatch] = useReducer(reducer, initialState, {
    type: "reset",
    payload: initialCount
  });

如您所见,第三个参数是要执行的初始操作,将在初始渲染期间应用。

例如:Codepan Example Link