本文介绍了在单独的文件中具有 React 上下文,无法让组件不重新渲染的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
我有一个简单的 React Context 示例,它使用 useMemo 来记忆一个函数,并在单击任何子组件时重新渲染所有子组件.我已经尝试了几种替代方案(已注释掉),但都没有奏效.请参阅 stackblitz 及下方的代码.
https://stackblitz.com/edit/react-yo4eth
Index.js
从反应"导入反应;import { render } from react-dom";从./Hello"导入你好;import { GlobalProvider } from "./GlobalState";功能应用(){返回 (<全球供应商><你好/></GlobalProvider>);}渲染(,document.getElementById(root"));
GlobalState.js
import React, {createContext、useState、useCallback、useMemo} 来自反应";export const GlobalContext = createContext({});export const GlobalProvider = ({ children }) =>{const [speakerList, setSpeakerList] = useState([{ name: Crockford", id: 101, 最喜欢的: true },{ name: Gupta", id: 102, 最喜欢的: false },{ name: Ailes", id: 103, 最喜欢的: true },]);const clickFunction = useCallback((speakerIdClicked) => {setSpeakerList((currentState) => {返回 currentState.map((rec) => {如果(rec.id === SpeakerIdClicked){return { ...rec, 最喜欢的:!rec.favorite };}返回记录;});});},[]);//const provider = useMemo(() => {//return { clickFunction: clickFunction, SpeakerList: SpeakerList };//}, []);//const provider = { clickFunction: clickFunction, SpeakerList: SpeakerList };常量提供者 = {clickFunction: useMemo(() => clickFunction,[]),演讲者列表:演讲者列表,};返回 (<GlobalContext.Provider value={provider}>{children}</GlobalContext.Provider>);};
Hello.js
import React, {useContext} from "react";从./Speaker"导入扬声器;从 './GlobalState' 导入 { GlobalContext };导出默认值 () =>{const { SpeakerList } = useContext(GlobalContext);返回 (<div>{speakerList.map((rec) => {return <Speaker Speaker={rec} key={rec.id}></Speaker>;})}
);};
Speaker.js
import React, { useContext } from react";import { GlobalContext } from "./GlobalState";导出默认 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 ?真实":假"}按钮></>);});
解决方案
代码中的几个问题:
解决方案:
因为您不想将 clickFunction
作为道具从 Hello
组件传递给 Speaker
组件并希望访问 clickFunction
直接在 Speaker
组件中,你可以为 clickFunction
创建一个单独的 Context
.
这会起作用,因为在单独的 Context
中提取 clickFunction
将允许 Speaker
组件不使用 GlobalContext
.当任何按钮被点击时,GlobalContext
将被更新,导致所有使用 GlobalContext
的组件重新渲染.由于 Speaker
组件正在使用一个未更新的单独上下文,因此当单击任何按钮时,它将阻止 Speaker
组件的所有实例重新呈现.
演示
const GlobalContext = React.createContext({});const GlobalProvider = ({ children }) =>{const [speakerList, setSpeakerList] = React.useState([{ name: "Crockford", id: 101, 最喜欢的: true },{ name: "Gupta", id: 102, 最喜欢的: false },{ 名称:Ailes",id:103,最喜欢的:true }]);返回 (<GlobalContext.Provider value={{speakerList, setSpeakerList }}>{孩子们}</GlobalContext.Provider>);};const ClickFuncContext = React.createContext();const ClickFuncProvider = ({ children }) =>{const { SpeakerList, setSpeakerList } = React.useContext(GlobalContext);const clickFunction = React.useCallback(speakerIdClicked => {setSpeakerList(currentState => {返回 currentState.map(rec => {如果(rec.id === SpeakerIdClicked){return { ...rec, 最喜欢的:!rec.favorite };}返回记录;});});}, []);返回 (<ClickFuncContext.Provider value={clickFunction}>{孩子们}</ClickFuncContext.Provider>);};const Speaker = React.memo(({ Speaker }) => {console.log(`speaker ${speaker.id} ${speaker.name} ${speaker.favorite}`);const clickFunction = React.useContext(ClickFuncContext)返回 (<div><按钮onClick={() =>{clickFunction(speaker.id);}}>{speaker.name} {speaker.id}{" "}{speaker.favorite === true ?真假"}按钮>
);});功能扬声器列表(){const { SpeakerList } = React.useContext(GlobalContext);返回 (<div>{speakerList.map(rec => {返回 (<Speaker Speaker={rec} key={rec.id}/>);})}
);};功能应用(){返回 (<全球供应商><ClickFuncProvider><发言人名单/></ClickFuncProvider></GlobalProvider>);}ReactDOM.render(, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script><div id="root"></div>
您还可以在 StackBlitz
I've got a simple example of React Context that uses useMemo to memoize a function and all child components re-render when any are clicked. I've tried several alternatives (commented out) and none work. Please see code at stackblitz and below.
https://stackblitz.com/edit/react-yo4eth
Index.js
import React from "react";
import { render } from "react-dom";
import Hello from "./Hello";
import { GlobalProvider } from "./GlobalState";
function App() {
return (
<GlobalProvider>
<Hello />
</GlobalProvider>
);
}
render(<App />, document.getElementById("root"));
GlobalState.js
import React, {
createContext,useState,useCallback,useMemo
} from "react";
export const GlobalContext = createContext({});
export const GlobalProvider = ({ children }) => {
const [speakerList, setSpeakerList] = useState([
{ name: "Crockford", id: 101, favorite: true },
{ name: "Gupta", id: 102, favorite: false },
{ name: "Ailes", id: 103, favorite: true },
]);
const clickFunction = useCallback((speakerIdClicked) => {
setSpeakerList((currentState) => {
return currentState.map((rec) => {
if (rec.id === speakerIdClicked) {
return { ...rec, favorite: !rec.favorite };
}
return rec;
});
});
},[]);
// const provider = useMemo(() => {
// return { clickFunction: clickFunction, speakerList: speakerList };
// }, []);
//const provider = { clickFunction: clickFunction, speakerList: speakerList };
const provider = {
clickFunction: useMemo(() => clickFunction,[]),
speakerList: speakerList,
};
return (
<GlobalContext.Provider value={provider}>{children}</GlobalContext.Provider>
);
};
Hello.js
import React, {useContext} from "react";
import Speaker from "./Speaker";
import { GlobalContext } from './GlobalState';
export default () => {
const { speakerList } = useContext(GlobalContext);
return (
<div>
{speakerList.map((rec) => {
return <Speaker speaker={rec} key={rec.id}></Speaker>;
})}
</div>
);
};
Speaker.js
import React, { useContext } from "react";
import { GlobalContext } from "./GlobalState";
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>
</>
);
});
解决方案
Couple of problems in your code:
You already have memoized the clickFunction
with useCallback
, no need to use useMemo
hook.
You are consuming the Context
in Speaker
component. That is what's causing the re-render of all the instances of Speaker
component.
Solution:
Since you don't want to pass clickFunction
as a prop from Hello
component to Speaker
component and want to access clickFunction
directly in Speaker
component, you can create a separate Context
for clickFunction
.
This will work because extracting clickFunction
in a separate Context
will allow Speaker
component to not consume GlobalContext
. When any button is clicked, GlobalContext
will be updated, leading to the re-render of all the components consuming the GlobalContext
. Since, Speaker
component is consuming a separate context that is not updated, it will prevent all instances of Speaker
component from re-rendering when any button is clicked.
Demo
const GlobalContext = React.createContext({});
const GlobalProvider = ({ children }) => {
const [speakerList, setSpeakerList] = React.useState([
{ name: "Crockford", id: 101, favorite: true },
{ name: "Gupta", id: 102, favorite: false },
{ name: "Ailes", id: 103, favorite: true }
]);
return (
<GlobalContext.Provider value={{ speakerList, setSpeakerList }}>
{children}
</GlobalContext.Provider>
);
};
const ClickFuncContext = React.createContext();
const ClickFuncProvider = ({ children }) => {
const { speakerList, setSpeakerList } = React.useContext(GlobalContext);
const clickFunction = React.useCallback(speakerIdClicked => {
setSpeakerList(currentState => {
return currentState.map(rec => {
if (rec.id === speakerIdClicked) {
return { ...rec, favorite: !rec.favorite };
}
return rec;
});
});
}, []);
return (
<ClickFuncContext.Provider value={clickFunction}>
{children}
</ClickFuncContext.Provider>
);
};
const Speaker = React.memo(({ speaker }) => {
console.log(`speaker ${speaker.id} ${speaker.name} ${speaker.favorite}`);
const clickFunction = React.useContext(ClickFuncContext)
return (
<div>
<button
onClick={() => {
clickFunction(speaker.id);
}}
>
{speaker.name} {speaker.id}{" "}
{speaker.favorite === true ? "true" : "false"}
</button>
</div>
);
});
function SpeakerList() {
const { speakerList } = React.useContext(GlobalContext);
return (
<div>
{speakerList.map(rec => {
return (
<Speaker speaker={rec} key={rec.id} />
);
})}
</div>
);
};
function App() {
return (
<GlobalProvider>
<ClickFuncProvider>
<SpeakerList />
</ClickFuncProvider>
</GlobalProvider>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You can also see this demo on StackBlitz
这篇关于在单独的文件中具有 React 上下文,无法让组件不重新渲染的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!