创建自定义钩子时了解useState和useReducer之间的区别

问题描述 投票:0回答:1

据我了解,reducer 除了成为 useState 的语法糖之外,在 React 中不会做任何事情。但实际上它的工作原理并没有什么不同。例如,假设我正在尝试创建一个自定义挂钩:

const update = (state, action) => {
  switch (action) {
    case 'increment':
      return state + 1
      break
    case 'decrement':
      return state - 1
      break
    default:
      break
  }
} 

const initState = 0

const useWithReducer = () => {
  const [state, dispatch] = useReducer(update, initState)
  return [state, dispatch]
}

const useWithState = () => {
  const [state, setState] = useState(initState)
  const dispatch = (action) => setState(update(state, action))
  return [state, dispatch]
}

这两件事是一样的。

据我所知,无论

initState
update()
有多复杂,reducer 和 state 版本应该始终工作相同。如果我将
initState
更新为
{nestedState: 0}
并相应更新 update ,它仍然可以工作。换句话说,useWithReducer 和 useWithState 在技术上是相同的/做相同的事情。这是正确的吗?对于这些项目在幕后的工作原理,我是否缺少一些东西?我唯一能想到的为什么
useState
可能会更好/不同的是:

const useWithState = () => {
  const [state, setState] = useState(initState)
  const dispatch = (action) => setState((currentState) => update(currentState, action))
  return [state, dispatch]
}
reactjs react-hooks
1个回答
0
投票

是的,你是对的,你展示的两段代码非常相似。如果我们看一下实现 useState 的源代码,它实际上是 useReducer 代码的包装器。

在第一次渲染时,

useState
创建一个调度函数,它作为索引1返回(然后通常由用户代码分配一个像
setFoo
这样的名称):

function mountState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  const hook = mountStateImpl(initialState);
  const queue = hook.queue;
  const dispatch: Dispatch<BasicStateAction<S>> = (dispatchSetState.bind(
    null,
    currentlyRenderingFiber,
    queue,
  ): any);
  queue.dispatch = dispatch;
  return [hook.memoizedState, dispatch];
}

然后在后续渲染中,useState 仅运行 useReducer 的代码:

function updateState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  return updateReducer(basicStateReducer, initialState);
}

我在您的代码中看到的与 React 代码的主要区别在于,您在每个渲染上创建一个新的调度函数,而 React 只执行一次。这可能会意外地破坏记忆。正如您在上一个示例中指出的那样,您还需要确保在最新的状态下进行操作,而 React 是开箱即用的。

© www.soinside.com 2019 - 2024. All rights reserved.