React useReducer Hook发射两次/如何将道具传递给reducer?

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

前言/说明

我正在尝试将React的新钩子功能用于我正在构建的电子商务网站,并且一直在解决我的购物车组件中的错误问题。

我认为与讨论前言有关,我试图通过使用多个Context组件来保持我的全局状态模块化。我有一个单独的上下文组件,用于我提供的项目类型,以及一个单独的上下文组件,用于个人购物车中的项目。

问题

我遇到的问题是,当我发送一个操作来将一个组件添加到我的购物车时,减速器将运行两次,就像我已经将项目添加到我的购物车两次一样。但只有在最初渲染时,或者出于奇怪的原因,例如将显示设置为hidden然后返回block或更改z-index以及可能的其他类似更改。

我知道这有点冗长,但它是相当针织的挑剔问题所以我创建了两个代码来展示这个问题:

full example

minimum example

您将看到我已经包含一个按钮来切换组件的display。这将有助于展示css与问题的相关性。

最后请在代码栏中监控控制台,这将显示所有按钮单击以及每个减速器的哪个部分已运行。这些问题在full example中最明显,但是控制台语句显示问题也出现在minimum example中。

问题区域

我已经确定问题与我使用useContext钩子的状态得到物品清单这一事实有关。调用一个函数来为我的useReducer钩子生成reducer,但只有在使用不同的钩子时才会出现。我可以使用一个不会像钩子那样重新评估的函数而且没有问题,但我也是需要我之前的Context中的信息,以便解决方法不能解决我的问题。

相关链接

我已经确定问题不是HTML问题所以我不会包含我尝试过的HTML修补程序的链接。这个问题虽然由css触发,但不是以css为根,所以我也不会包含css链接。

useReducer Action dispatched twice

javascript reactjs react-hooks react-context
1个回答
10
投票

正如您所指出的,原因与您所链接的related answer相同。每当Provider被重新渲染时,你就会重新创建你的reducer,所以在某些情况下React会执行reducer以确定它是否需要重新渲染Provider,如果它确实需要重新渲染它会检测到reducer被更改,因此React需要执行新的reducer并使用它生成的新状态,而不是先前版本的reducer返回的状态。

当你不能仅仅因为依赖于道具或上下文或其他状态而将reducer从你的函数组件中移出时,解决方案是使用useCallback来记忆你的reducer,这样你只能在它的依赖变化时创建一个新的reducer(例如productsList)在你的情况下)。

另外要记住的是,您不必过于担心减速器执行两次单次调度。 React的假设是减速器通常足够快(它们不能做任何副作用,进行API调用等),在某些情况下需要重新执行它们的风险是值得的。为了避免不必要的重新渲染(如果在带有reducer的元素下面有一个大的元素层次结构,这可能比reducer更昂贵)。

这是使用ProvideruseCallback的修改版本:

const Context = React.createContext();
const Provider = props => {
  const memoizedReducer = React.useCallback(createReducer(productsList), [productsList])
  const [state, dispatch] = React.useReducer(memoizedReducer, []);

  return (
    <Context.Provider value={{ state, dispatch }}>
      {props.children}
    </Context.Provider>
  );
}

以下是您的codepen的修改版本:https://codepen.io/anon/pen/xBdVMp?editors=0011

以下是与useCallback相关的几个答案,如果您不熟悉如何使用此钩子,可能会有所帮助:

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