react系列-React.memo
组件渲染
什么时候会触发组件渲染
当传入组件的 props 和组件内部 state 更新时会触发组件渲染。
当父组件发生渲染就会触发子组件的渲染,如果是 class 组件则可以使用 React.PureComponent 进行优化,如果是函数组件则可以使用 React.memo 进行优化,减少渲染次数。
值得阅读的文章
React.memo
与普通的 FC 进行比较
普通的 FC 和被 React.memo 包裹后的 FC 渲染次数对比:
function TestComponent(props) {
console.log('我重新渲染了');
return <div>函数组件:1</div>;
}
const MyComponent = React.memo(function MyComponent(props) {
console.log('memo: 我重新渲染了');
return <div>memo函数组件:2</div>;
});
console.log(MyComponent);
class LifeCycle extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
text: '这个子组件文本',
message: '这个是子组件消息'
};
}
changeText = () => {
this.setState({
text: '修改后的子组件文本'
});
};
changeMessage = () => {
this.setState({
message: '修改后的子组件消息'
});
};
render() {
console.log('render LifeCycle class component');
return (
<div>
<button onClick={this.changeText}>
修改子组件文本内容
</button>
<button onClick={this.changeMessage}>
修改子组件消息
</button>
<div>{this.state.text}</div>
<div>{this.state.message}</div>
<div className='textContent'>
<TestComponent />
<MyComponent />
</div>
</div>
);
}
}
class LifeCycelContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
text: '父组件文本',
message: '父组件消息'
};
}
changeText = () => {
this.setState({
text: '修改后的父组件文本'
});
};
changeMessage = () => {
this.setState({
message: '修改后的父组件消息'
});
};
render() {
return (
<div>
<button onClick={this.changeText}>
修改父组件文本
</button>
<button onClick={this.changeMessage}>
修改父组件消息
</button>
<div>{this.state.text}</div>
<div>{this.state.message}</div>
<LifeCycle />
</div>
);
}
}
ReactDOM.render(<LifeCycelContainer />, document.getElementById('root'));
复杂的 props 数据结构
如果 props 数据结构过于复杂,React.memo 只进行浅层比较,也会触发多次渲染的问题。
function TestComponent(props) {
console.log('我重新渲染了');
return <div>函数组件:1</div>;
}
const MyComponent = React.memo(function MyComponent(props) {
console.log('memo: 我重新渲染了', props.prop);
return <div>memo函数组件:2</div>;
});
class LifeCycle extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
text: '这个子组件文本',
message: '这个是子组件消息',
count: 1,
};
}
changeText = () => {
this.setState({
text: '修改后的子组件文本'
});
};
changeMessage = () => {
this.setState({
message: '修改后的子组件消息'
});
};
addCount = () => {
this.setState({
count: this.state.count + 1
})
};
getMonthlyDisableDate = (date) => {
return true;
};
getChildProps = () => {
return [
{
fieldName: 'reportId',
type: 'select',
label: '月报名称',
clearable: false,
value: [],
},
{
fieldName: 'reportId2',
type: 'select2',
label: '月报名称2',
clearable: false,
value: [],
disable: this.getMonthlyDisableDate,
},
{
fieldName: 'reportId2',
type: 'select2',
label: '月报名称2',
clearable: false,
value: [],
noResultText: () => (
<div>
暂无数据,去添加
</div>
),
}
];
}
render() {
console.log('render LifeCycle class component');
return (
<div>
<button onClick={this.changeText}>
修改子组件文本内容
</button>
<button onClick={this.changeMessage}>
修改子组件消息
</button>
<button onClick={this.addCount}>
+1
</button>
<div>{this.state.text}</div>
<div>{this.state.message}</div>
<div>count: {this.state.count}</div>
<div className='textContent'>
<TestComponent />
<MyComponent prop={this.getChildProps()} />
</div>
</div>
);
}
}
class LifeCycelContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
text: '父组件文本',
message: '父组件消息'
};
}
changeText = () => {
this.setState({
text: '修改后的父组件文本'
});
};
changeMessage = () => {
this.setState({
message: '修改后的父组件消息'
});
};
render() {
return (
<div>
<button onClick={this.changeText}>
修改父组件文本
</button>
<button onClick={this.changeMessage}>
修改父组件消息
</button>
<div>{this.state.text}</div>
<div>{this.state.message}</div>
<LifeCycle />
</div>
);
}
}
ReactDOM.render(<LifeCycelContainer />, document.getElementById('root'));
React.memo 会对每次传入的 props 做浅层比较。比较算法 shallowEqual :
对基础数据格式、只有一层的数组或对象可以进行比较,如果包含函数或其他复杂的数据类型,则返回结果则为 false。
参考链接:Object.is() - MDN
自定义设置比较函数
React.memo 有第二个参数,可以自定义深层比较函数。
使用自定义的对比函数,改写上面的 demo:
function deepCompareObject(prevValue, nextValue) {
if (Object.is(prevValue, nextValue)) {
return true;
}
if (typeof prevValue !== typeof nextValue) {
return false;
}
if (['string', 'number', 'boolean', 'undefined'].includes(typeof prevValue)) {
return false;
}
if (prevValue === null || nextValue === null) {
return false;
}
if (typeof prevValue === 'function' && typeof nextValue === 'function') {
return prevValue.toString() === nextValue.toString();
}
// object & array
if (typeof prevValue === 'object') {
const prevValueKeys = Object.keys(prevValue);
const nextValueKeys = Object.keys(nextValue);
if (prevValueKeys.length !== nextValueKeys.length) {
return false;
}
for (let i = 0; i < prevValueKeys.length; i++) {
if (!Object.prototype.hasOwnProperty.call(nextValue, prevValueKeys[i])) {
return false;
}
const result = deepCompareObject(prevValue[prevValueKeys[i]], nextValue[prevValueKeys[i]]);
if (!result) {
return false;
}
}
return true;
}
return false;
}
function TestComponent(props) {
console.log('我重新渲染了');
return <div>函数组件:1</div>;
}
const MyComponent = React.memo(function MyComponent(props) {
console.log('memo: 我重新渲染了');
return <div>memo函数组件:2</div>;
}, deepCompareObject);
class LifeCycle extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
text: '这个子组件文本',
message: '这个是子组件消息',
count: 1,
};
}
changeText = () => {
this.setState({
text: '修改后的子组件文本'
});
};
changeMessage = () => {
this.setState({
message: '修改后的子组件消息'
});
};
addCount = () => {
this.setState({
count: this.state.count + 1
})
};
getMonthlyDisableDate = (date) => {
return true;
};
getChildProps = () => {
return [
{
fieldName: 'reportId',
type: 'select',
label: '月报名称',
clearable: false,
value: [],
},
{
fieldName: 'reportId2',
type: 'select2',
label: '月报名称2',
clearable: false,
value: [],
disable: this.getMonthlyDisableDate,
},
{
fieldName: 'reportId2',
type: 'select2',
label: '月报名称2',
clearable: false,
value: [],
noResultText: () => (
<div>
暂无数据,去添加
</div>
),
}
];
}
render() {
console.log('render LifeCycle class component');
return (
<div>
<button onClick={this.changeText}>
修改子组件文本内容
</button>
<button onClick={this.changeMessage}>
修改子组件消息
</button>
<button onClick={this.addCount}>
+1
</button>
<div>{this.state.text}</div>
<div>{this.state.message}</div>
<div>count: {this.state.count}</div>
<div className='textContent'>
<TestComponent />
<MyComponent value={this.getChildProps()} />
</div>
</div>
);
}
}
class LifeCycelContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
text: '父组件文本',
message: '父组件消息'
};
}
changeText = () => {
this.setState({
text: '修改后的父组件文本'
});
};
changeMessage = () => {
this.setState({
message: '修改后的父组件消息'
});
};
render() {
return (
<div>
<button onClick={this.changeText}>
修改父组件文本
</button>
<button onClick={this.changeMessage}>
修改父组件消息
</button>
<div>{this.state.text}</div>
<div>{this.state.message}</div>
<LifeCycle />
</div>
);
}
}
ReactDOM.render(<LifeCycelContainer />, document.getElementById('root'));