PureComponent
一、认识
PureComponent
对 props
和 state
进行浅层或者引用地址比较。通过比较后,如果前后发生变化触发组件重新渲染,如果前后没有变化则不重新渲染。
二、背景
在日常开发中,有很多React 父组件引用React 子组件的场景,React 父组件 状态的更新,会触发React 子组件重新渲染,无论父组件是否有传值给子组件。如下所示:
import React from "react";
import ReactDOM from "react-dom";
class ParentComponent extends React.Component {
state = { number: 0 };
handleClick = (e) => {
this.setState({ number: this.state.number + 1 });
};
render() {
console.log("父组件");
return (
<div>
<p>{this.state.number}</p>
<button onClick={this.handleClick}>增加</button>
<ChildrenComponent />
</div>
);
}
}
class ChildrenComponent extends React.Component {
render() {
console.log("子组件");
return <div></div>;
}
}
ChildrenComponent = React.memo(ChildrenComponent);
ReactDOM.render(<ParentComponent />, document.getElementById("root"));
现象: 每次点击按钮,父组件状态发生变化,子组件都没有接收父组件的任何状态,但是子组件也还是触发更新了。这样对于性能是很不好的
解决: 我们希望的情形是: React 父组件 某一个状态更新,只有接收这个状态的子组件会触发更新渲染,如果没有接收这个状态,则不会触发更新渲染。
三、语法
import React from "react";
import ReactDOM from "react-dom";
class ParentComponent extends React.PureComponent {
state = { number: 0 };
handleClick = (e) => {
this.setState({ number: this.state.number + 1 });
};
render() {
console.log("父组件");
return (
<div>
<p>{this.state.number}</p>
<button onClick={this.handleClick}>增加</button>
<ChildrenComponent />
</div>
);
}
}
class ChildrenComponent extends React.PureComponent {
render() {
console.log("子组件");
return <div></div>;
}
}
ReactDOM.render(<ParentComponent />, document.getElementById("root"));
四、对比
4.1 React.PureComponent 与 React.Component 对比
-
Component
特点-
1.
Rect.Component
没有实现shouldComponentUpdate()
-
2. 如果子组件使用
React.Component
,只要父组件重新渲染,那么就会触发子组件渲染
-
-
PureComponent
特点-
1.
React.PureComponent
中以浅层对比prop
和state
的方式来实现了shouldComponentUpdate
-
2. 如果子组件使用
React.PureComponent
, 父组件中只有传入子组件的状态发生变化时才会触发子组件重新渲染
-
五、注意事项
5.1 PureComponent 组件避免使用箭头函数
描述: 父组件render
PureComponent子组件时, 不可使用箭头函数,如下所示:
import React, { PureComponent,Component } from "react";
import ReactDOM from "react-dom";
class Child extends PureComponent {
constructor(props) {
super(props);
}
render() {
console.log("Child 组件渲染");
return (
<div>
<h3>Child 组件</h3>
</div>
);
}
}
class App extends Component {
render() {
return (
<div>
<h3>App 组件</h3>
<Child callback={() => {}} />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
结论: 如果是箭头函数绑定的话,都会重新生成一个新的箭头函数,PureComponent
对比新老 props
时候,因为是新的函数,所以会判断不相等,而让组件直接渲染,PureComponent
作用终会失效
5.2 PureComponent 组件的父组件为函数组件时,传递的函数要用 useCallback 或者 useMemo 处理
描述: 如果父组件是函数,子组件是 PureComponent
,那么绑定函数要小心,因为函数组件每一次执行,如果不处理,还会声明一个新的函数,所以 PureComponent
对比同样会失效, 如下所示:
import React, { PureComponent } from "react";
import ReactDOM from "react-dom";
class Child extends PureComponent {
constructor(props) {
super(props);
}
render() {
console.log("Child 组件渲染");
return (
<div>
<h3>Child 组件</h3>
</div>
);
}
}
function App() {
return (
<div>
<h3>App 组件</h3>
<Child />
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
所以,可以用 useCallback
或者 useMemo
解决这个问题,useCallback
首选,这个 hooks
初衷就是为了解决这种情况的
使用useCallback
来缓存函数:
import React, { PureComponent, useCallback, useState } from "react";
import ReactDOM from "react-dom";
class Child extends PureComponent {
constructor(props) {
super(props);
}
render() {
const { callback } = this.props;
console.log(callback);
console.log("Child 组件渲染");
return (
<div>
<h3>Child 组件</h3>
</div>
);
}
}
function App() {
const [num, setNum] = useState(0);
const callback = useCallback(() => {
console.log("哈哈");
}, []);
return (
<div>
<h3>App 组件</h3>
{num}
<button onClick={() => setNum(num + 1)}>改变</button>
<Child callback={callback} />
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
使用useMemo
来缓存函数:
import React, { PureComponent, useMemo, useState } from "react";
import ReactDOM from "react-dom";
class Child extends PureComponent {
constructor(props) {
super(props);
}
render() {
const { callback } = this.props;
console.log(callback);
console.log("Child 组件渲染");
return (
<div>
<h3>Child 组件</h3>
</div>
);
}
}
function App() {
const [num, setNum] = useState(0);
const callback = useMemo(() => {
return () => {
console.log("哈哈");
};
}, []);
return (
<div>
<h3>App 组件</h3>
{num}
<button onClick={() => setNum(num + 1)}>改变</button>
<Child callback={callback} />
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));