在 nextjs 中使用 memo 避免不必要的组件渲染
Avoid unnecessary component rendering with memo in nextjs
我正在尝试通过 nextjs 了解 React 的行为。
我有一个 index.js
页面,其中一个组件 Homecard
显示了三次,一个按钮增加了一个值。
每次单击按钮时,所有 Homecard
组件都会重新渲染。
index.js
import { Homecard } from '../components/Homecard'
import { useState } from 'react'
export default function Home() {
const [count, increment] = useState(0);
const homecards = [
{
"main": "main0",
"sub": "sub0",
"desc": "Desc0",
"nav": [{
"href": "/page0",
"value": "see"
}]
},
{
"main": "main1",
"sub": "sub1",
"desc": "Desc1",
"nav": [{
"href": "/page1",
"value": "see"
}]
},
{
"main": "main2",
"sub": "sub2",
"desc": "Desc2",
"nav": [{
"href": "/page2",
"value": "see"
}]
}
];
const handleCount = () => {
increment(count => count + 1);
}
return (
<>
<div className='d-flex justify-content-between' style={{ marginLeft: '-1.5rem' }}>
{
homecards.map((homecard, index) => (
<Homecard
key={index}
main={homecard.main}
sub={homecard.sub}
desc={homecard.desc}
nav={homecard.nav}
/>
))
}
</div>
<button onClick={handleCount}>increment {count}</button>
</>
)
}
homecard.js
export default function Homecard({ main, sub, desc, nav }) {
console.log('render Homecard');
return (
<div className={`${styles.homecard}`}>
<div>
<h3>
{main}
{sub &&
<span>{sub}</span>
}
</h3>
<p>{desc}</p>
{nav &&
<ul>
{nav.map((link, index) => (
<li key={index}>
<Link href={link.href}>
<a>{link.value}</a>
</Link>
</li>
))}
</ul>
}
</div>
</div>
)
}
我试着用 React.memo 包裹我的 Homecard
const Homecard = React.memo(({ main, sub, desc, nav }) => {})
但是当我点击我的按钮时,我仍然看到 console.log('render Homecard');
。
我怎样才能只更新我的按钮而不更新其他组件?
问题是您在 Home
的每个渲染器上 重新创建 您的 homecards
数组,因此每个 nav
对象都是一个new 对象,所以 React.memo
看到了 props 的不同,并没有优化掉随后的 re-renders.
有几种方法可以修复它:
在Home
外定义homecards
;它是不变的,因此没有理由在每次调用 Home
时重新创建它。
如果由于某种原因你不能这样做,你可以将第二个参数传递给 React.memo
,“areEqual
”函数,它将接收旧的道具和新的;在函数中,进行深入比较,看看是否 nav
个对象具有相同的 属性 值,即使它们是不同的对象。
#1 示例:
const { useState } = React;
/*export default*/ const Homecard = React.memo(({ img, main, sub, desc, nav }) => {
console.log('render Homecard');
return (
<div className={`${""/*styles.homecard*/}`}>
<div>
<h3>
{main}
{sub &&
<span>{sub}</span>
}
</h3>
<p>{desc}</p>
{nav &&
<ul>
{nav.map((link, index) => (
<li key={index}>
<a href={link.href}>
{link.value}
</a>
</li>
))}
</ul>
}
</div>
</div>
)
});
const homecards = [
{
"main": "main0",
"sub": "sub0",
"desc": "Desc0",
"nav": [{
"href": "/page0",
"value": "see"
}]
},
{
"main": "main1",
"sub": "sub1",
"desc": "Desc1",
"nav": [{
"href": "/page1",
"value": "see"
}]
},
{
"main": "main2",
"sub": "sub2",
"desc": "Desc2",
"nav": [{
"href": "/page2",
"value": "see"
}]
}
];
/*export default*/ function Home() {
const [count, increment] = useState(0);
const handleCount = () => {
increment(count => count + 1);
}
return (
// Sadly, Stack Snippets don't support <>...</>
<React.Fragment>
<div className='d-flex justify-content-between' style={{ marginLeft: '-1.5rem' }}>
{
homecards.map((homecard, index) => (
<Homecard
key={index}
main={homecard.main}
sub={homecard.sub}
desc={homecard.desc}
nav={homecard.nav}
/>
))
}
</div>
<button onClick={handleCount}>increment {count}</button>
</React.Fragment>
)
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Home />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
(警告:为了在 Stack Snippets 中完成这项工作,我做了一些小改动,比如替换 Link
并使用 React.Fragment
而不是 <>...</>
,所以不要将其直接复制并粘贴回您的项目,只需将 homecards
移动到您现有的代码中即可。)
我正在尝试通过 nextjs 了解 React 的行为。
我有一个 index.js
页面,其中一个组件 Homecard
显示了三次,一个按钮增加了一个值。
每次单击按钮时,所有 Homecard
组件都会重新渲染。
index.js
import { Homecard } from '../components/Homecard'
import { useState } from 'react'
export default function Home() {
const [count, increment] = useState(0);
const homecards = [
{
"main": "main0",
"sub": "sub0",
"desc": "Desc0",
"nav": [{
"href": "/page0",
"value": "see"
}]
},
{
"main": "main1",
"sub": "sub1",
"desc": "Desc1",
"nav": [{
"href": "/page1",
"value": "see"
}]
},
{
"main": "main2",
"sub": "sub2",
"desc": "Desc2",
"nav": [{
"href": "/page2",
"value": "see"
}]
}
];
const handleCount = () => {
increment(count => count + 1);
}
return (
<>
<div className='d-flex justify-content-between' style={{ marginLeft: '-1.5rem' }}>
{
homecards.map((homecard, index) => (
<Homecard
key={index}
main={homecard.main}
sub={homecard.sub}
desc={homecard.desc}
nav={homecard.nav}
/>
))
}
</div>
<button onClick={handleCount}>increment {count}</button>
</>
)
}
homecard.js
export default function Homecard({ main, sub, desc, nav }) {
console.log('render Homecard');
return (
<div className={`${styles.homecard}`}>
<div>
<h3>
{main}
{sub &&
<span>{sub}</span>
}
</h3>
<p>{desc}</p>
{nav &&
<ul>
{nav.map((link, index) => (
<li key={index}>
<Link href={link.href}>
<a>{link.value}</a>
</Link>
</li>
))}
</ul>
}
</div>
</div>
)
}
我试着用 React.memo 包裹我的 Homecard
const Homecard = React.memo(({ main, sub, desc, nav }) => {})
但是当我点击我的按钮时,我仍然看到 console.log('render Homecard');
。
我怎样才能只更新我的按钮而不更新其他组件?
问题是您在 Home
的每个渲染器上 重新创建 您的 homecards
数组,因此每个 nav
对象都是一个new 对象,所以 React.memo
看到了 props 的不同,并没有优化掉随后的 re-renders.
有几种方法可以修复它:
在
Home
外定义homecards
;它是不变的,因此没有理由在每次调用Home
时重新创建它。如果由于某种原因你不能这样做,你可以将第二个参数传递给
React.memo
,“areEqual
”函数,它将接收旧的道具和新的;在函数中,进行深入比较,看看是否nav
个对象具有相同的 属性 值,即使它们是不同的对象。
#1 示例:
const { useState } = React;
/*export default*/ const Homecard = React.memo(({ img, main, sub, desc, nav }) => {
console.log('render Homecard');
return (
<div className={`${""/*styles.homecard*/}`}>
<div>
<h3>
{main}
{sub &&
<span>{sub}</span>
}
</h3>
<p>{desc}</p>
{nav &&
<ul>
{nav.map((link, index) => (
<li key={index}>
<a href={link.href}>
{link.value}
</a>
</li>
))}
</ul>
}
</div>
</div>
)
});
const homecards = [
{
"main": "main0",
"sub": "sub0",
"desc": "Desc0",
"nav": [{
"href": "/page0",
"value": "see"
}]
},
{
"main": "main1",
"sub": "sub1",
"desc": "Desc1",
"nav": [{
"href": "/page1",
"value": "see"
}]
},
{
"main": "main2",
"sub": "sub2",
"desc": "Desc2",
"nav": [{
"href": "/page2",
"value": "see"
}]
}
];
/*export default*/ function Home() {
const [count, increment] = useState(0);
const handleCount = () => {
increment(count => count + 1);
}
return (
// Sadly, Stack Snippets don't support <>...</>
<React.Fragment>
<div className='d-flex justify-content-between' style={{ marginLeft: '-1.5rem' }}>
{
homecards.map((homecard, index) => (
<Homecard
key={index}
main={homecard.main}
sub={homecard.sub}
desc={homecard.desc}
nav={homecard.nav}
/>
))
}
</div>
<button onClick={handleCount}>increment {count}</button>
</React.Fragment>
)
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Home />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
(警告:为了在 Stack Snippets 中完成这项工作,我做了一些小改动,比如替换 Link
并使用 React.Fragment
而不是 <>...</>
,所以不要将其直接复制并粘贴回您的项目,只需将 homecards
移动到您现有的代码中即可。)