javascript - 嵌套组件中的 React.memo 从不调用 areEqual,总是重新渲染
问题描述
我对 React.memo 今天的行为方式感到好奇。
似乎 React.memo 在嵌套函数式组件中不起作用,但在组件主返回和返回 React.memo 组件的嵌套函数中起作用。
这是预期的行为还是有其他方法可以使 React.memo 与嵌套的功能组件一起工作?
https://codesandbox.io/s/sweet-bhaskara-kbnlg?file=/src/memoTest.js:0-451
const MemoTestBase = ({ name, counter }) => {
console.log("rendering", name);
return (
<div className="">
<p>
{name}: {counter}
</p>
</div>
);
};
function areEqual(prevProps, nextProps) {
const isEqual = prevProps.name === nextProps.name;
if (isEqual) {
console.log("stopping", prevProps.name);
}
return isEqual;
}
const MemoTest = React.memo(MemoTestBase, areEqual);
const { useState } = React;
function App() {
const [counter, setCounter] = useState(0);
function funOne() {
return <MemoTest name="inside function" counter={counter} />;
}
const ComponentOne = () => {
return <MemoTest name="inside component" counter={counter} />;
};
return (
<div className="App">
<h1>React.memo render test</h1>
<p>Count: {counter}</p>
<button onClick={() => setCounter(counter + 1)}>Count + 1</button>
<hr />
<p>Memo tests</p>
<MemoTest name="root" counter={counter} /> {/* works! */}
<ComponentOne /> {/* doesn't work */}
{ComponentOne()} {/* works! */}
{funOne()} {/* works! */}
</div>
);
}
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class='react'></div>
解决方案
这里的问题是您在组件内部定义了 ComponentOne。因此,当 React 尝试重新渲染时,它会发现先前重新渲染中的 ComponentOne 引用与当前重新渲染中的引用不同,因此会重新安装该组件。因此备忘录不起作用
但是,当您像函数一样调用它时,它会返回相同的实例MemoTest
,因此应该会发生重新渲染,memo
因为道具不会改变
为了使它像您当前定义的那样工作,您应该将功能组件定义移出 App 组件
const MemoTestBase = ({ name, counter }) => {
console.log("rendering", name);
return (
<div className="">
<p>
{name}: {counter}
</p>
</div>
);
};
function areEqual(prevProps, nextProps) {
const isEqual = prevProps.name === nextProps.name;
if (isEqual) {
console.log("stopping", prevProps.name);
}
return isEqual;
}
const MemoTest = React.memo(MemoTestBase, areEqual);
const ComponentOne = ({counter}) => {
return <MemoTest name="inside component" counter={counter} />;
};
const { useState } = React;
function App() {
const [counter, setCounter] = useState(0);
function funOne() {
return <MemoTest name="inside function" counter={counter} />;
}
return (
<div className="App">
<h1>React.memo render test</h1>
<p>Count: {counter}</p>
<button onClick={() => setCounter(counter + 1)}>Count + 1</button>
<hr />
<p>Memo tests</p>
<MemoTest name="root" counter={counter} /> {/* works! */}
<ComponentOne counter={counter}/> {/* doesn't work */}
{ComponentOne({counter})} {/* works! */}
{funOne()} {/* works! */}
</div>
);
}
ReactDOM.render(<App />, document.querySelector('.react'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div class='react'></div>
推荐阅读
- python - 加速 Pandas Dataframe groupby 聚合的最佳方法
- unity3d - iOS 横向模式下的插页式 Unity 广告崩溃
- html - 如何在 django html 主页模板的主页上仅显示图像列表/集中的 1 张图像?
- javascript - 使用 TensorFlow JS 使用字符串作为输入来预测特征
- google-cloud-platform - 如何使用 python 获取 GCP vm 实例总内存大小?
- wpf - 作为单个窗口打开一个新的 WPF 视图
- python - 为什么类大多是通过函数实例化的?
- laravel - 在 Laravel 5.8.38 中上传多个文件时数组到字符串的转换错误
- java - 将两个while循环代码转换为java 8流代码
- visual-studio - 为变量赋值时出现 Blazor 或 Visual Studio 错误