簡體   English   中英

React useReducer Hook 觸發兩次/如何將道具傳遞給減速器?

[英]React useReducer Hook fires twice / how to pass props to reducer?

前言/描述

我正在嘗試將 React 的新鈎子功能用於我正在構建的電子商務網站,並且在處理我的購物車組件中的錯誤時遇到了問題。

我認為以我試圖通過使用多個 Context 組件來保持我的全局狀態模塊化這一事實作為討論的開頭是相關的。 我有一個單獨的上下文組件用於我提供的項目類型,還有一個單獨的上下文組件用於一個人的購物車中的項目。

問題

我遇到的問題是,當我調度一個動作來將一個組件添加到我的購物車時,reducer 會運行兩次,就好像我已經將物品添加到我的購物車兩次一樣。 但僅當它最初被渲染時,或者出於奇怪的原因,例如將顯示設置為hidden然后返回到blockz-index更改以及可能的其他類似更改。

我知道這有點冗長,但這是一個相當挑剔的問題,所以我創建了兩個代碼筆來展示這個問題:

完整示例

最小的例子

您會看到我包含了一個按鈕來切換組件的display 這將有助於展示 css 與問題的相關性。

最后請在代碼筆中監控控制台,這將顯示所有按鈕點擊以及每個減速器的哪個部分已經運行。 這些問題在完整示例中最為明顯,但顯示該問題的控制台語句也出現在最小示例中

問題區

我已經指出問題與我使用useContext鈎子的狀態來獲取項目列表這一事實有關。 一個函數被調用來為我的useReducer鈎子生成減速器,但只有在使用不同的鈎子時才會出現,也就是我可以使用一個不會像鈎子一樣重新評估並且沒有問題的函數,但我也需要我以前的上下文中的信息,以便解決方法並不能真正解決我的問題。

相關鏈接

我已確定該問題不是 HTML 問題,因此我不會包含指向我嘗試過的 HTML 修復程序的鏈接。 該問題雖然由 css 觸發,但並非根源於 css,因此我也不會包含 css 鏈接。

useReducer Action 分派兩次

正如您所指出的,原因您鏈接到的我的相關答案相同。 每當重新渲染Provider時,您都在重新創建減速器,因此在某些情況下,React 將執行減速器以確定它是否需要重新渲染Provider ,如果確實需要重新渲染,它會檢測到reducer 改變了,所以 React 需要執行新的 reducer 並使用它產生的新狀態,而不是之前版本的 reducer 返回的狀態。

當由於對 props 或上下文或其他狀態的依賴而無法將 reducer 移出函數組件時,解決方案是使用useCallback您的 reducer,以便僅在其依賴項發生變化時才創建新的 reducer(例如productsList在你的情況下)。

要記住的另一件事是,您不必太擔心您的減速器為一次調度執行兩次。 React 所做的假設是,reducer 通常足夠快(它們不能做任何有副作用的事情,進行 API 調用等),因此在某些場景中需要重新執行它們的風險是值得的為了盡量避免不必要的重新渲染(如果在帶有 reducer 的元素下面有一個大的元素層次結構,這可能比 reducer 昂貴得多)。

這是使用useCallbackProvider的修改版本:

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相關的幾個答案,如果您不熟悉如何使用此鈎子,它們可能會有所幫助:

將 Reducer 與幫助我解決問題的功能組件分開

一個基於 Ryans 優秀答案的例子。

  const memoizedReducer = React.useCallback((state, action) => {
    switch (action.type) {
      case "addRow":
        return [...state, 1];
      case "deleteRow":
        return [];
      default:
        throw new Error();
    }
  }, []) // <--- if you have vars/deps inside the reducer that changes, they need to go here

  const [data, dispatch] = React.useReducer(memoizedReducer, _data);

當我閱讀一些useContext源代碼時,我發現

const useContext = hook(class extends Hook {
  call() {
    if(!this._ranEffect) {
      this._ranEffect = true;
      if(this._unsubscribe) this._unsubscribe();
      this._subscribe(this.Context);
      this.el.update();
    }
  }

第一次更新后,更新后會調用一個effect like。 在將value訂閱到正確的上下文后,例如,解析來自Provider的值,它會請求另一個更新。 由於_ranEffect標志,這不是循環。

在我看來,如果上面的React是真的,渲染引擎會被調用兩次。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM