如何避免 React Component 不必要的重新渲染
How to avoid unnecessary re-rendering of React Component
我一直在学习 React 16.8 的新特性。我相信 React 的 Pure Component 应该会自动避免不必要的重新渲染操作。
在下面的例子中,App
本身就是一个无状态组件。我使用 useState
维护两个状态对象 text
和 nested: {text}
.
有3个测试。前 2 个测试有效。无论我改变多少次状态,都不需要重新渲染操作。
现在,第三个测试尝试使用相同的字符串值设置 text
的状态,但引用不同。我不希望重新渲染任何内容,但实际上 <Headline/>
将被重新渲染。
我是不是要用一些死记硬背的技巧来避免?我觉得归档它的代码太多了。程序员必须非常小心才能编写高质量的 React 代码。 ..
class Headline extends React.PureComponent {
render() {
const {text} = this.props;
return <h1>{text} (render time: {Date.now()})</h1>;
}
}
const simpleText = 'hello world'
const App = () => {
const [text, setText] = React.useState(simpleText)
const [nested, setNested] = React.useState({text: simpleText})
return (
<div>
<Headline text={text}/>
<Headline text={nested.text}/>
<button onClick={()=>setText(simpleText)}>
test 1: the first line should not change (expected)
</button>
<button onClick={()=>setNested({text: simpleText})}>
test 2: the second line will not change (expected)
</button>
<button onClick={()=>setText(new String(simpleText))}>
test 3: the first line will change on every click (why?)
</button>
</div>
)
}
ReactDOM.render(<App />, document.querySelector("#app"))
这是 jsfiddle 中的现场游乐场:
https://jsfiddle.net/fL0psxwo/1/
谢谢 React 朋友们,干杯!
更新 1:
感谢丹尼斯提到 why-did-you-render
作者点了一些很有用的文章。我认为这对每个人都非常有教育意义。
https://medium.com/welldone-software/why-did-you-render-mr-big-pure-react-component-part-2-common-fixing-scenarios-667bfdec2e0f
更新 2:
我创建了一个名为 withDirtyCheck
的新挂钩,以便我的代码自动执行内容脏检查。
import isEqual from 'lodash-es/isEqual';
export const withDirtyCheck = ([getter, setter]) => {
const setStateIfDirty = (nextState) =>
setter((prevState) => (isEqual(prevState, nextState) ? prevState : nextState));
return [getter, setStateIfDirty];
};
问题是 new
operator 你创建的 String Object
总是与之前的状态不同。
'hello world' === new String('hello world') // false, always.
'hello world' === String('hello world') // true
检查这个 example:
setText(prevState => {
// Will render
// const currState = new String(simpleText);
// Won't render
const currState = String(simpleText);
console.log(prevState === currState); // if true, no re-render
// if false, re-render
return currState;
});
参考What is the difference between string primitives and String objects in JavaScript?
我一直在学习 React 16.8 的新特性。我相信 React 的 Pure Component 应该会自动避免不必要的重新渲染操作。
在下面的例子中,App
本身就是一个无状态组件。我使用 useState
维护两个状态对象 text
和 nested: {text}
.
有3个测试。前 2 个测试有效。无论我改变多少次状态,都不需要重新渲染操作。
现在,第三个测试尝试使用相同的字符串值设置 text
的状态,但引用不同。我不希望重新渲染任何内容,但实际上 <Headline/>
将被重新渲染。
我是不是要用一些死记硬背的技巧来避免?我觉得归档它的代码太多了。程序员必须非常小心才能编写高质量的 React 代码。 ..
class Headline extends React.PureComponent {
render() {
const {text} = this.props;
return <h1>{text} (render time: {Date.now()})</h1>;
}
}
const simpleText = 'hello world'
const App = () => {
const [text, setText] = React.useState(simpleText)
const [nested, setNested] = React.useState({text: simpleText})
return (
<div>
<Headline text={text}/>
<Headline text={nested.text}/>
<button onClick={()=>setText(simpleText)}>
test 1: the first line should not change (expected)
</button>
<button onClick={()=>setNested({text: simpleText})}>
test 2: the second line will not change (expected)
</button>
<button onClick={()=>setText(new String(simpleText))}>
test 3: the first line will change on every click (why?)
</button>
</div>
)
}
ReactDOM.render(<App />, document.querySelector("#app"))
这是 jsfiddle 中的现场游乐场:
https://jsfiddle.net/fL0psxwo/1/
谢谢 React 朋友们,干杯!
更新 1: 感谢丹尼斯提到 why-did-you-render
作者点了一些很有用的文章。我认为这对每个人都非常有教育意义。 https://medium.com/welldone-software/why-did-you-render-mr-big-pure-react-component-part-2-common-fixing-scenarios-667bfdec2e0f
更新 2:
我创建了一个名为 withDirtyCheck
的新挂钩,以便我的代码自动执行内容脏检查。
import isEqual from 'lodash-es/isEqual';
export const withDirtyCheck = ([getter, setter]) => {
const setStateIfDirty = (nextState) =>
setter((prevState) => (isEqual(prevState, nextState) ? prevState : nextState));
return [getter, setStateIfDirty];
};
问题是 new
operator 你创建的 String Object
总是与之前的状态不同。
'hello world' === new String('hello world') // false, always.
'hello world' === String('hello world') // true
检查这个 example:
setText(prevState => {
// Will render
// const currState = new String(simpleText);
// Won't render
const currState = String(simpleText);
console.log(prevState === currState); // if true, no re-render
// if false, re-render
return currState;
});
参考What is the difference between string primitives and String objects in JavaScript?