小编典典

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

reactjs

前言/说明

我正在尝试将React的新挂钩函数用于我正在构建的电子商务网站,但是在解决购物车组件中的错误时遇到了问题。

我认为以我想通过使用多个Context组件保持全局状态模块化的事实作为开始讨论的重点。对于我提供的商品类型,我有一个单独的上下文组件,对于一个人的购物车中的商品,我有一个单独的上下文组件。

问题

我遇到的问题是,当我调度将组件添加到购物车的操作时,减速器将运行两次,就像我两次将商品添加到购物车一样。但是仅当它最初被渲染时,或者由于诸如显示之类的奇怪原因而被设置为hidden,然后又返回到block或由于或z-index可能的其他类似更改而发生更改。

我知道这很冗长,但它是一个很挑剔的问题,因此我创建了两个演示该问题的代码笔:

完整的例子

最小的例子

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

最后,请用代码笔监视控制台,这将显示所有按钮单击以及每个reducer的哪一部分已运行。该问题在完整示例中最明显,但控制台语句显示的问题在最小示例中也存在。

问题区域

我已查明该问题与以下事实有关:我正在使用useContext挂钩的状态来获取项目列表。调用了一个函数来为我的useReducer钩子生成化简器,但是仅当使用了另一个钩子时才出现,我可以使用像钩子一样不会重新评估的函数,并且没有问题,但是我还需要我以前的上下文中的信息,因此解决方法无法真正解决我的问题。

相关连结

我确定问题不是HTML问题,因此我将不包含我尝试过的HTML修复程序的链接。该问题虽然是由CSS触发的,但并非植根于CSS,因此我也不会包含CSS链接。


阅读 801

收藏
2020-07-22

共1个答案

小编典典

正如您指出的,原因与您链接到的我的相关答案相同。每当Provider重新渲染时,您都在重新创建化简器,因此在某些情况下,React将执行化简器以确定是否需要重新渲染Provider,如果确实需要重新渲染,它将检测到该化简器更改了,因此React需要执行新的reducer并使用它产生的新状态,而不是先前版本的reducer返回的状态。

当由于对prop或上下文或其他状态的依赖而无法仅将reducer移出功能组件时,解决方案是使用来记住您的reducer
useCallback,以便仅在其依赖关系发生变化时(例如productsList,在案件)。

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

这是Providerusing 的修改版本useCallback

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

2020-07-22