簡體   English   中英

React Context Consumer 不會被動更改或重新渲染

[英]React Context Consumer not changing reactively or rerendering

我有一個奇怪的情況,狀態變量在一個組件中成功更新,但在另一個組件中沒有。 我成功地改變了state ,因為我可以看到它反映在我觸發dispatch的組件中。 但是在包裝在同一個Provider中的另一個組件中,我看不到任何變化。

其他 Stack Overflow 答案似乎主要建議不要直接改變 state ,但在這里我沒有這樣做 - 我正在調度一個操作來更新狀態,類似於您在 Redux 語法中看到的內容。

代碼沙盒演示

TeaSettingsContext.tsx

const TeaSettingsContext = createContext<{ state: State; dispatch: Dispatch } | undefined>(undefined);

const teaSettingsReducer = (state: State, action: Action) => {
  switch (action.type) {
    case TeaSettingsActions.ChooseTea: {
      return { ...state, chosenTea: action.payload };
    }
    case TeaSettingsActions.ChangeStrength: {
      return { ...state, desiredStrength: action.payload };
    }
    default: {
      throw new Error(`Unhandled action type: ${action.type}`);
    }
  }
};

export function TeaSettingsProvider({
  children,
}: TeaSettingsProviderProps): Context {
  const [state, dispatch] = useReducer(teaSettingsReducer, {
    chosenTea: {},
    desiredStrength: 0.5,
  });
  return (
    <TeaSettingsContext.Provider value={{ state, dispatch }}>
      {children}
    </TeaSettingsContext.Provider>
  );
}

export const useTeaSettingsContext = (): TeaSettingsContext => {
  const context = useContext(TeaSettingsContext);
  if (context === undefined) {
    return;
  }
  return context;
};

這是“成功”的組成部分:

主頁.tsx

<TeaSettingsProvider>
  <StrengthSlider />
</TeaSettingsProvider>

強度滑塊.tsx

export default function StrengthSlider(): ReactNode {
  const { state, dispatch } = useTeaSettingsContext();
  console.log(state.desiredStrength) // logs the increment on each press.

  const incrementStrength = () => {
    dispatch({
      payload: state.desiredStrength + 0.1,
      type: "change-strength",
    });
  };

  return (
    <Box position="relative" w="100%">
      <Button title="Press" onPress={incrementStrength} />
      <Text>{state.desiredStrength}</Text>
    </Box>
  );
}

...並且不成功的重新渲染發生在此組件中:

茶頁.tsx

const Component = () => {
    if (type === "tea") {
      return data.map((teaObj) => (
        <TeaCard id={teaObj.id} teaData={teaObj.data} key={teaObj.id} />
      ));
    }
  };

  return (
    <TeaSettingsProvider>
      <Component />
    </TeaSettingsProvider>
  );

茶卡.tsx

export function TeaCard({ id, teaData }: TeaCardProps): ReactNode {
  const { state, dispatch } = useTeaSettingsContext();
  console.log(state.desiredStrength); // this always logs the starting value of 0.5 and doesn't log each time I press the button above.
// ...
}

僅供參考:我的代碼基於 Kent C Dodds 的文章: https : //kentcdodds.com/blog/how-to-use-react-context-effectively

我認為您誤解了上下文的工作原理。

基本上(如反應文檔所建議的那樣):

每個 Context 對象都帶有一個 Provider React 組件,該組件允許消費組件訂閱上下文更改。

Provider 組件接受要傳遞給作為此 Provider 后代的消費組件的 value prop。 一個提供者可以連接到多個消費者。 提供者可以嵌套以覆蓋樹中更深層次的值。

每當 Provider 的 value 屬性發生變化時,所有作為 Provider 后代的消費者都會重新渲染。 從 Provider 到其后代消費者(包括 .contextType 和 useContext)的傳播不受 shouldComponentUpdate 方法的影響,因此即使祖先組件跳過更新,消費者也會更新。

簡而言之,當您創建一個具有值的提供者時,您可以從多個消費者(需要是該提供者的孩子)訪問該信息。 到現在為止還挺好。

除了: Home.tsxTeaPage.tsx都初始化了一個不同的上下文實例(所以每個實例都有自己的提供者)。 為了解決這個問題,您應該只在HomeTeaPage父組件中創建一個提供程序。

React 上下文不能作為 redux(你有一個可以從任何地方訪問的全局存儲)。 它讓您可以選擇為大組件(在子組件中使用)創建上下文(使用可重用數據)。

另請注意開頭引用的最后一段:基本上當上下文發生變化時,這會導致所有消費者重新渲染(有時可能會導致一些性能問題),因此請明智地使用它。 是一篇關於此的簡短文章。

暫無
暫無

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

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