React useReducer Hook发射两次/如何将道具传递给reducer? [英] React useReducer Hook fires twice / how to pass props to reducer?

查看:721
本文介绍了React useReducer Hook发射两次/如何将道具传递给reducer?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

前言/描述

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

I am trying to use React's new hooks feature for an e-commerce website that I am building, and have been having an issue working a bug out of my shopping cart component.

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

I think it is relevant to preface the discussion with the fact that I am trying to keep my global state modular by using multiple Context components. I have a separate context component for the types of items that I offer, and a separate context component for the items in a person's shopping cart.

问题

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

The issue I am having is that when I dispatch an action to add a component to my cart, the reducer will run twice as if I had added the item to my cart twice. But only when it is initially rendered, or for weird reasons such as the display is set to hidden and then back to block or for a change in the z-index and potentially other similar changes.

我知道这是一种详细,但它是相当针织的挑剔问题所以我创建了两个代码来展示问题:

I know this is kind of verbose, but it is rather knit picky issue so I have created two codepens that showcase the issue:

完整示例

最小示例

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

You will see that I have included a button to toggle the display of the components. This will help showcase the correlation of the css to the issue.

最后请在代码栏中监控控制台,这将显示所有按钮点击以及每个减速器的哪个部分已经运行了。这些问题在完整示例中最明显,但控制台语句显示问题也存在在最低示例中。

Finally please monitor the console in the code pens, this will show all button clicks and which part of each reducer has been run. The issues are most evident in the full example, but the console statements display the issue is also present in the minimum example.

问题区域

我已经确定问题与我使用<$ c的状态这一事实有关$ c> useContext hook获取项目列表。调用一个函数来为我的 useReducer 钩子生成reducer,但只有在使用不同的钩子时才会出现这种情况。我可以使用一个不会重新接受的函数。像钩子这样的eval并没有问题,但我也需要我之前的Context中的信息,以便解决方法不能解决我的问题。

I have pinpointed the problem to be related to the fact that I am using the state of a useContext hook to get the items list. A function is called to generate the reducer for my useReducer hook, but only arises when a different hook is used AKA I could use a function that wouldn't be subject to re-eval like hook is and not have the issue, but I also need the info from my previous Context so that workaround doesn't really fix my issue.

相关链接

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

I have determined the issue is NOT an HTML issue so I will not include the links to the HTML fixes I have tried. The issue, while triggered by css, is not rooted in css so I will not include css links either.

useReducer动作调度两次

推荐答案

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

As you indicated, the cause is the same as the related answer of mine that you linked to. You are re-creating your reducer whenever Provider is re-rendered, so in some cases React will execute the reducer in order to determine whether or not it needs to re-render Provider and if it does need to re-render it will detect that the reducer is changed, so React needs to execute the new reducer and use the new state produced by it rather than what was returned by the previous version of the reducer.

当你因为依赖于props或context或其他状态而无法将reducer移出你的函数组件时,解决方案是使用 useCallback ,这样您只能在其依赖项发生变化时创建一个新的reducer(例如 productsList )。

When you can't just move the reducer out of your function component due to dependencies on props or context or other state, the solution is to memoize your reducer using useCallback, so that you only create a new reducer when its dependencies change (e.g. productsList in your case).

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

The other thing to keep in mind is that you shouldn't worry too much about your reducer executing twice for a single dispatch. The assumption React is making is that reducers are generally going to be fast enough (they can't do anything with side effects, make API calls, etc.) that it is worth the risk of needing to re-execute them in certain scenarios in order to try to avoid unnecessary re-renders (which could be much more expensive than the reducer if there is a large element hierarchy underneath the element with the reducer).

这里是 Provider 的修改版本,使用 useCallback

Here's a modified version of Provider using 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

Here is a modified version of your codepen: https://codepen.io/anon/pen/xBdVMp?editors=0011

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

Here are a couple answers related to useCallback that might be helpful if you aren't familiar with how to use this hook:

  • Trouble with simple example of React Hooks useCallback
  • React Hooks useCallback causes child to re-render

这篇关于React useReducer Hook发射两次/如何将道具传递给reducer?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆