使用 React Context API 传递 React 函数时重新渲染的问题 [英] Problem with Re-rendering when passing a React function with React Context API

查看:75
本文介绍了使用 React Context API 传递 React 函数时重新渲染的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个简单的例子,我将一个 clickFunction 作为值传递给 React Context,然后在子组件中访问该值.尽管我使用的是 React.memo 和 React.useCallback,但该子组件会重新呈现事件.我在 stackblitz 中有一个例子,它没有重新渲染问题没有在此处使用上下文:

https://stackblitz.com/edit/react-y5w2cp(没问题这个)

但是,当我添加上下文并将函数作为上下文值的一部分传递时,所有子组件都会重新渲染.此处显示问题的示例:

https://stackblitz.com/edit/react-wpnmuk

这是问题代码:

Hello.js

import React, { useCallback, useState, createContext } from react";从./Speaker"导入扬声器;export const GlobalContext = createContext({});导出默认值 () =>{const 扬声器阵列 = [{ name: Crockford", id: 101, 最喜欢的: true },{ name: Gupta", id: 102, 最喜欢的: false },{ name: Ailes", id: 103, 最喜欢的: true },];const [speakers, setSpeakers] = useState(speakersArray);const clickFunction = useCallback((speakerIdClicked) => {setSpeakers((currentState) =>currentState.map((rec) => {如果(rec.id === SpeakerIdClicked){return { ...rec, 最喜欢的:!rec.favorite };}返回记录;}));}, []);返回 (<GlobalContext.Provider价值={{clickFunction: memoizedValue,}}>{speakers.map((rec) => {return <Speaker Speaker={rec} key={rec.id}></Speaker>;})}</GlobalContext.Provider>);};

Speaker.js

import React, {useContext} from "react";import { GlobalContext } from "./Hello";导出默认 React.memo(({ Speaker }) => {console.log(`speaker ${speaker.id} ${speaker.name} ${speaker.favorite}`);const { clickFunction } = useContext(GlobalContext);返回 (<按钮onClick={() =>{clickFunction(speaker.id);}}>{speaker.name} {speaker.id} {speaker.favorite === true ?真实":假"});});

以下答案中的工作代码

Speaker.js

import React, { useContext } from "react";

import { GlobalContext } from "./Hello";导出默认 React.memo(({ Speaker }) => {console.log(`speaker ${speaker.id} ${speaker.name} ${speaker.favorite}`);const { clickFunction } = useContext(GlobalContext);返回 (<按钮onClick={() =>{clickFunction(speaker.id);}}>{speaker.name} {speaker.id} {speaker.favorite === true ?真实":假"});});

Hello.js

import React, { useState, createContext, useMemo } from react";从./Speaker"导入扬声器;export const GlobalContext = createContext({});导出默认值 () =>{const 扬声器阵列 = [{ name: Crockford", id: 101, 最喜欢的: true },{ name: Gupta", id: 102, 最喜欢的: false },{ name: Ailes", id: 103, 最喜欢的: true },];const [speakers, setSpeakers] = useState(speakersArray);const clickFunction = (speakerIdClicked) =>{setSpeakers((currentState) =>currentState.map((rec) => {如果(rec.id === SpeakerIdClicked){return { ...rec, 最喜欢的:!rec.favorite };}返回记录;}));};const provider = useMemo(() => {返回 ({clickFunction: clickFunction});}, []);返回 (<GlobalContext.Provider value={provider}>{speakers.map((rec) => {return <Speaker Speaker={rec} key={rec.id}></Speaker>;})}</GlobalContext.Provider>);};

解决方案

当将 value={{clickFunction}} 作为 prop 传递给 Provider 时,当组件重新渲染并将重新创建此对象时,这将使子项更新,所以为了防止这种情况您需要使用 useMemo 记住该值.

代码如下:

import React, { useCallback, useState, createContext,useMemo } from react";从./Speaker"导入扬声器;export const GlobalContext = createContext({});导出默认值 () =>{const 扬声器阵列 = [{ name: Crockford", id: 101, 最喜欢的: true },{ name: Gupta", id: 102, 最喜欢的: false },{ name: Ailes", id: 103, 最喜欢的: true },];const [speakers, setSpeakers] = useState(speakersArray);const clickFunction = useCallback((speakerIdClicked) => {setSpeakers((currentState) =>currentState.map((rec) => {如果(rec.id === SpeakerIdClicked){return { ...rec, 最喜欢的:!rec.favorite };}返回记录;}));}, []);const provider =useMemo(()=>({clickFunction}),[])返回 (<div>{speakers.map((rec) => {返回 (<GlobalContext.Provider value={provider}><扬声器扬声器={rec}键={rec.id}></扬声器></GlobalContext.Provider>);})}

);};

注意你不再需要使用 useCallback clickFunction

I have a simple example where I pass a clickFunction as a value to React Context and then access that value in a child component. That child component re-renders event though I'm using React.memo and React.useCallback. I have an example in stackblitz that does not have the re-render problem without using context here:

https://stackblitz.com/edit/react-y5w2cp (no problem with this)

But, when I add context and pass the the function as part of the value of the context, all children component re-render. Example showing problem here:

https://stackblitz.com/edit/react-wpnmuk

Here is the problem code:

Hello.js

import React, { useCallback, useState, createContext } from "react";

import Speaker from "./Speaker";

export const GlobalContext = createContext({});

export default () => {
  const speakersArray = [
    { name: "Crockford", id: 101, favorite: true },
    { name: "Gupta", id: 102, favorite: false },
    { name: "Ailes", id: 103, favorite: true },
  ];

  const [speakers, setSpeakers] = useState(speakersArray);

  const clickFunction = useCallback((speakerIdClicked) => {
    setSpeakers((currentState) =>
      currentState.map((rec) => {
        if (rec.id === speakerIdClicked) {
          return { ...rec, favorite: !rec.favorite };
        }
        return rec;
      })
    );
  }, []);

  return (
    <GlobalContext.Provider
      value={{
        clickFunction: memoizedValue,
      }}
    >
      {speakers.map((rec) => {
        return <Speaker speaker={rec} key={rec.id}></Speaker>;
      })}
    </GlobalContext.Provider>
  );
};

Speaker.js

import React, {useContext} from "react";

import { GlobalContext } from "./Hello";

export default React.memo(({ speaker }) => {
  console.log(`speaker ${speaker.id} ${speaker.name} ${speaker.favorite}`);

  const { clickFunction } = useContext(GlobalContext);

  return (
    <button
      onClick={() => {
        clickFunction(speaker.id);
      }}
    >
      {speaker.name} {speaker.id} {speaker.favorite === true ? "true" : "false"}
    </button>
  );
});

WORKING CODE BELOW FROM ANSWERS BELOW

Speaker.js

import React, { useContext } from "react";

import { GlobalContext } from "./Hello";

export default React.memo(({ speaker }) => {
  console.log(`speaker ${speaker.id} ${speaker.name} ${speaker.favorite}`);

  const { clickFunction } = useContext(GlobalContext);

  return (
    <button
      onClick={() => {
        clickFunction(speaker.id);
      }}
    >
      {speaker.name} {speaker.id} {speaker.favorite === true ? "true" : "false"}
    </button>
  );
});

Hello.js

import React, { useState, createContext, useMemo } from "react";

import Speaker from "./Speaker";

export const GlobalContext = createContext({});

export default () => {
  const speakersArray = [
    { name: "Crockford", id: 101, favorite: true },
    { name: "Gupta", id: 102, favorite: false },
    { name: "Ailes", id: 103, favorite: true },
  ];

  const [speakers, setSpeakers] = useState(speakersArray);

  const clickFunction = (speakerIdClicked) => {
    setSpeakers((currentState) =>
      currentState.map((rec) => {
        if (rec.id === speakerIdClicked) {
          return { ...rec, favorite: !rec.favorite };
        }
        return rec;
      })
    );
  };
  const provider = useMemo(() => {
    return ({clickFunction: clickFunction});
  }, []);
  return (
    <GlobalContext.Provider value={provider}>
      {speakers.map((rec) => {
        return <Speaker speaker={rec} key={rec.id}></Speaker>;
      })}
    </GlobalContext.Provider>
  );
};

解决方案

when passing value={{clickFunction}} as prop to Provider like this when the component re render and will recreate this object so which will make child update, so to prevent this you need to memoized the value with useMemo.

here the code:

import React, { useCallback, useState, createContext,useMemo } from "react";

import Speaker from "./Speaker";

export const GlobalContext = createContext({});

export default () => {
  const speakersArray = [
    { name: "Crockford", id: 101, favorite: true },
    { name: "Gupta", id: 102, favorite: false },
    { name: "Ailes", id: 103, favorite: true },
  ];

  const [speakers, setSpeakers] = useState(speakersArray);

  const clickFunction = useCallback((speakerIdClicked) => {
    setSpeakers((currentState) =>
      currentState.map((rec) => {
        if (rec.id === speakerIdClicked) {
          return { ...rec, favorite: !rec.favorite };
        }
        return rec;
      })
    );
  }, []);
const provider =useMemo(()=>({clickFunction}),[])
  return (
    <div>
      {speakers.map((rec) => {
        return (
          <GlobalContext.Provider value={provider}>
            <Speaker
              speaker={rec}
              key={rec.id}
            ></Speaker>
          </GlobalContext.Provider>
        );
      })}
    </div>
  );
};

note you dont need to use useCallback anymore clickFunction

这篇关于使用 React Context API 传递 React 函数时重新渲染的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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