使用 React Context API 传递 React 函数时重新渲染的问题 [英] Problem with Re-rendering when passing a React function with React Context API
问题描述
我有一个简单的例子,我将一个 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屋!