内容来自 Dan Abramov
如果state更新缓慢
- 确保程序运行在生产环境中
- 确保state没有放在比实际需要更高的位置
- 通过react开发工具等方法检测导致二次渲染的原因
- 在高开销的子树上或其他需要的地方包裹useMemo
可以不用useMemo的情况
如下例,当App中的color变化时,我们会重新渲染一次被我们手动大幅延缓渲染的组件。
import { useState } from 'react';
export default function App() {
let [color, setColor] = useState('red');
return (
<div>
<input value={color} onChange={(e) => setColor(e.target.value)} />
<p style={{ color }}>Hello, world!</p>
<ExpensiveTree />
</div>
);
}
function ExpensiveTree() {
let now = performance.now();
while (performance.now() - now < 100) {
// Artificial delay -- do nothing for 100ms
}
return <p>I am a very slow component tree.</p>;
}
这时可以用useMemo来优化,但下面介绍2种不需要使用useMemo的方法
1. 向下移动State
仔细看一下渲染代码,你会注意到返回的树中只有一部分真正关心当前的color:
let [color, setColor] = useState('red');
……
<input value={color} onChange={(e) => setColor(e.target.value)} />
<p style={{ color }}>Hello, world!</p>
所以让我们把这一部分提取到Form组件中然后将state移动到该组件里:
export default function App() {
return (
<>
<Form />
<ExpensiveTree />
</>
);
}
function Form() {
let [color, setColor] = useState('red'); return (
<>
<input value={color} onChange={(e) => setColor(e.target.value)} />
<p style={{ color }}>Hello, world!</p>
</>
);
}
2. 内容提升
当一部分state在高开销树的上层代码中使用时上述解法就无法奏效了。举个例子,如果我们将color放到父元素div中:
export default function App() {
let [color, setColor] = useState('red');
return (
<div style={{ color }}>
<input value={color} onChange={(e) => setColor(e.target.value)} />
<p>Hello, world!</p>
<ExpensiveTree />
</div>
);
}
怎么办呢?我们可以将App组件分割为两个子组件。依赖color的代码就和color state变量一起放入ColorPicker组件里。 不关心color的部分就依然放在App组件中,然后以JSX内容的形式传递给ColorPicker,也被称为children属性。 当color变化时,ColorPicker会重新渲染。但是它仍然保存着上一次从App中拿到的相同的children属性,所以React并不会访问那棵子树。 因此,ExpensiveTree不会重新渲染。
export default function App() {
return (
<ColorPicker>
<p>Hello, world!</p>
<ExpensiveTree />
</ColorPicker>
);
}
function ColorPicker({ children }) {
let [color, setColor] = useState("red");
return (
<div style={{ color }}>
<input value={color} onChange={(e) => setColor(e.target.value)} />
{children}
</div>
);
}