Reactjs React with useCallback和UseMoom中的重新呈现问题
我有一个相当简单的组件Hello.js示例,它呈现了三个组件,每个组件都有一个不同的id Speaker.js。我有一个从Speaker.js传回的clickFunction。我认为使用React.memo和React.useCallback会在只有一个更改时阻止所有三个重新渲染,但遗憾的是,您可以从Speaker.js中的console.log中看到,单击这三个按钮中的任何一个都会导致所有三个渲染 以下是stackblitz的问题示例: 你好 Speaker.js 因为当您启动clickFunction时,它会更新扬声器,这会导致重新创建此函数,所以要解决此问题,您需要从clickFunction依赖项中删除扬声器,并从setState callback访问扬声器。 以下是解决方案: 从React导入React,{useCallback,useState,useEffect}Reactjs React with useCallback和UseMoom中的重新呈现问题,reactjs,react-hooks,Reactjs,React Hooks,我有一个相当简单的组件Hello.js示例,它呈现了三个组件,每个组件都有一个不同的id Speaker.js。我有一个从Speaker.js传回的clickFunction。我认为使用React.memo和React.useCallback会在只有一个更改时阻止所有三个重新渲染,但遗憾的是,您可以从Speaker.js中的console.log中看到,单击这三个按钮中的任何一个都会导致所有三个渲染 以下是stackblitz的问题示例: 你好 Speaker.js 因为当您启动clickFun
import Speaker from "./Speaker";
export default () => {
const [speakers, setSpeakers] = useState([
{ name: "Crockford", id: 101, favorite: true },
{ name: "Gupta", id: 102, favorite: false },
{ name: "Ailes", id: 103, favorite: true },
]);
const clickFunction = useCallback((speakerIdClicked) => {
setSpeakers(currentState=>currentState.map((rec) => {
if (rec.id === speakerIdClicked) {
rec.favorite = !rec.favorite;
return {...rec};
}
return rec
}));
},[]);
useEffect(()=>{
console.log("render")
})
return (
<div>
{speakers.map((rec) => {
return (
<Speaker
speaker={rec}
key={rec.id}
clickFunction={clickFunction}
></Speaker>
);
})}
</div>
);
};
对于扬声器组件:
import React from "react";
export default React.memo(({ speaker, clickFunction }) => {
return (
<button
onClick={() => {
clickFunction(speaker.id);
}}
>
{speaker.name} {speaker.id} {speaker.favorite === true ? "true" : "false"}
</button>
);
});
因为当您启动clickFunction时,它会更新扬声器,这会导致重新创建此函数,所以要解决此问题,您需要从clickFunction依赖项中删除扬声器,并从setState callback访问扬声器。
以下是解决方案:
从React导入React,{useCallback,useState,useEffect}
import Speaker from "./Speaker";
export default () => {
const [speakers, setSpeakers] = useState([
{ name: "Crockford", id: 101, favorite: true },
{ name: "Gupta", id: 102, favorite: false },
{ name: "Ailes", id: 103, favorite: true },
]);
const clickFunction = useCallback((speakerIdClicked) => {
setSpeakers(currentState=>currentState.map((rec) => {
if (rec.id === speakerIdClicked) {
rec.favorite = !rec.favorite;
return {...rec};
}
return rec
}));
},[]);
useEffect(()=>{
console.log("render")
})
return (
<div>
{speakers.map((rec) => {
return (
<Speaker
speaker={rec}
key={rec.id}
clickFunction={clickFunction}
></Speaker>
);
})}
</div>
);
};
对于扬声器组件:
import React from "react";
export default React.memo(({ speaker, clickFunction }) => {
return (
<button
onClick={() => {
clickFunction(speaker.id);
}}
>
{speaker.name} {speaker.id} {speaker.favorite === true ? "true" : "false"}
</button>
);
});
经过进一步思考,我认为我的答案可能并不完全正确:如果没有[演讲者]的依赖性,这将无法按预期工作 两件事: 传递给useCallback的[speakers]依赖项会导致函数在每次speakers更改时重新创建,并且由于回调本身调用setSpeakers,因此它将在每次渲染时重新创建 如果修复1,扬声器组件将根本不会重新渲染,因为它们接收相同的扬声器道具。speaker.favorite已更改的事实不会触发重新渲染,因为speaker仍然是同一个对象。要解决此问题,请使用click函数返回已翻转收藏夹的rec副本,而不是仅在现有对象中切换它:
经过进一步思考,我认为我的答案可能并不完全正确:如果没有[演讲者]的依赖性,这将无法按预期工作 两件事: 传递给useCallback的[speakers]依赖项会导致函数在每次speakers更改时重新创建,并且由于回调本身调用setSpeakers,因此它将在每次渲染时重新创建 如果修复1,扬声器组件将根本不会重新渲染,因为它们接收相同的扬声器道具。speaker.favorite已更改的事实不会触发重新渲染,因为speaker仍然是同一个对象。要解决此问题,请使用click函数返回已翻转收藏夹的rec副本,而不是仅在现有对象中切换它:
谢谢帮了大忙。我仍然得到一个我不理解的警告:第22:5行:React Hook useCallback缺少一个依赖项:“speakers”。包括它或删除依赖项数组react hooks/deps overrideMethod@react\u devtools\u backend.js:2273 printWarnings@webpackHotDevClient.js:138 handleWarnings@webpackHotDevClient.js:143 push../node\u modules/react dev utils/webpackHotDevClient.js.connection.onmessage@webpackHotDevClient.js:210。进一步思考后,我认为我的答案可能不完全正确:如果没有[speakers]依赖项,这将无法按预期工作。请检查我的答案,您可以从setState callback访问当前状态,以便useCallback不会依赖它,因为它可以从依赖项中删除。谢谢。帮了大忙。我仍然得到一个我不理解的警告:第22:5行:React Hook useCallback缺少一个依赖项:“speakers”。包括它或删除依赖项数组react hooks/deps overrideMethod@react\u devtools\u backend.js:2273 printWarnings@webpackHotDevClient.js:138 handleWarnings@webpackHotDevClient.js:143 push../node\u modules/react dev utils/webpackHotDevClient.js.connection.onmessage@webpackHotDevClient.js:210。进一步思考后,我认为我的答案可能不完全正确:如果没有[speakers]依赖项,这将无法按预期工作。请检查我的答案,您可以从setState callback访问当前状态,这样useCallback就不会依赖它,因为它可以从依赖项中删除。谢谢@adel。感谢您抽出额外的时间返回并改进答案。第一次你是如何让它工作的,我一直在苦苦挣扎,但第二次很容易,因为这只是点击功能的改变。这真的是我下一个问题的导论,直到我让这个问题工作了,我才能问这个问题。如果你有时间看一下,它几乎是一样的。谢谢@adel。感谢您抽出额外的时间返回并改进答案。第一次你是如何让它工作的,我一直在苦苦挣扎,但第二次很容易,因为这只是点击功能的改变。这真的是我下一个问题的导论,直到我让这个问题工作了,我才能问这个问题。如果你有时间看一下,它几乎是一样的。
import React, { useCallback, useState } from "react";
import Speaker from "./Speaker";
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) => {
var speakersArrayUpdated = speakers.map((rec) => {
if (rec.id === speakerIdClicked) {
return { ...rec, favorite: !rec.favorite }; // <= return a copy of rec
}
return rec;
});
setSpeakers(speakersArrayUpdated);
}, []); // <= remove speakers dependency
return (
<div>
{speakers.map((rec) => {
return (
<Speaker
speaker={rec}
key={rec.id}
clickFunction={clickFunction}
></Speaker>
);
})}
</div>
);
};