对于 Class 组件,不管我们的 state 变没变,只要执行this.setState都会调用render
export default class App extends React.Component {
constructor() {
super();
this.state = {
count: 1
};
}
onClick = () => {
this.setState(this.state);
};
render() {
console.log("render 执行了");
return (
<div>
{this.state.count}
<button onClick={this.onClick}>+1</button>
</div>
);
}
}
上面代码每次点击+1就会执行render
函数组件如果对应 state 的引用没变的话,就不会调用render
export default function App () {
const [state, setState] = useState({ count: 1 });
const onClick = () => {
setState(state) // 不会变
setState({count: state.count + 1})
}
return (
<div>
{state.count}
<button onClick={onClick}>+1</button>
</div>
)
}
使用 shouldComponentUpdate
阻止多余的 render
class App extends React.Component {
constructor() {
super();
this.state = {
count: 1,
other: "其他属性"
};
}
onClick = () => {
this.setState({
...this.state,
count: 1
});
};
shouldComponentUpdate(newProps, newState) {
for (let key in newState) {
if (newState[key] !== this.state[key]) {
return true; // state 变了就 render
}
}
for (let key in newProps) {
if (newProps[key] !== this.props[key]) {
return true; // props 变了就 render
}
}
console.log("此处阻止 render");
return false; // 没变就阻止 render
}
render() {
console.log("render 执行了");
return (
<div>
{this.state.count}
<button onClick={this.onClick}>+1</button>
</div>
);
}
}
我们也可以使用pureComponent 代替 shouldComponentUpdate
class App extends React.PureComponent {
constructor() {
super();
this.state = {
count: 1,
other: "其他属性"
};
}
onClick = () => {
this.setState({
...this.state,
count: 1
});
};
render() {
console.log("render 执行了");
return (
<div>
{this.state.count}
<button onClick={this.onClick}>+1</button>
</div>
);
}
}
使用 immer.js 修改深层级 state
上面如果我们的state层级很深,我们修改起来就会很困难,这时候我们可以使用 immer.js
,它会让我们在创建新对象写起来就像是在修改原对象
const newData = produce(data, (draft) => {
draft.user.group.name = "group 2";
});
代码中的 draft 实际上是个 proxy,会把你的修改收集起来,然后创建新对象,而且 immer 创建新对象的方法很智能,并不是直接深拷贝
immer.js 如何得到新的数据
上面我们修改 state.user.group.name 它只会把我们需要修改的这四个节点变成新的节点,其他的都直接复制过来(浅拷贝),所以我们图中左右两个 other 是同一个对象,只有在你修改 other 时才会创建新的对象,immer.js 永远不会去修改原数据
该用 Class 组件还是函数组件?
总结
state 内容变了的时候
引用可以不变 state.user.group.name = 'xxx'(不推荐)
引用可以变 state = {...state, user: xxx}(推荐用 immer)何时 render*
Class 组件看到一个或多个 setState 就 render
函数组件的发现 state 引用变了才会 renderrender 做了什么
render 会创建虚拟 DOM,经过 diff,最后选择性地更新真实 DOM
所以多余的 render 很可能不会浪费多少性能,没必要阻止
如果阻止 render 的代码如果太复杂,可能比 render 还要耗性能immer.js 的用法
像修改原数据一样,但实际得到新数据
新数据保有部分旧数据的引用,不会浪费内存
与函数组件非常契合为什么不能直接修改 state
因为出 bug 的概率更大