使用hooks 已经有一段时间了,虽然团队都已经可以熟练应用到项目,但是没有深入理解hooks 的意思。state , useEffect 滥用,造成了多余的多次渲染。
通过一个实现来了解下调用一个组件的一生,先看一下整个demo的样子。
整个结构是父组件调用红框子组件,子组件有一个title 是父组件传过来的属性,另一个subtitle 是一个state 按钮是刷新这个state 。
<div className={styles.container}>
<Button
type="primary"
onClick={() => {
setTitle((c) => {
return c + 'change';
});
}}
>
刷新子组件title
</Button>
<Button
style={{"marginLeft":'30px'}}
type="primary"
onClick={() => {
setVisible(false);
}}
>
卸载子组件
</Button>
{visible && <ReturnView title={title}></ReturnView>}
</div>
import { Button } from 'antd';
import React, { useEffect, useState } from 'react';
const IndexPage: React.FC<{ title: string }> = ({ title }) => {
const [subTitle, setSubTitle] = useState('subTitle');
useEffect(() => {
console.log('初始化加载');
return () => {
console.log('退出卸载');
};
}, []);
useEffect(() => {
console.log(`${subTitle}渲染完成之后`);
return () => {
console.log(`清除上次${subTitle}渲染`);
};
}, [subTitle]);
useEffect(() => {
console.log(`${title}渲染完成之后`);
return () => {
console.log(`清除上次${title}渲染`);
};
}, [title]);
console.log('页面刷新');
return (
<div style={{ border: '5px solid red', marginTop: '16px' }}>
<div>我是子组件</div>
<div>{title}</div>
<div>{subTitle}</div>
<Button type="primary" onClick={() => setSubTitle('subTitle Change')}>
{' '}
刷新subtitle
</Button>
</div>
);
};
export default IndexPage;
在子组件加了一些打印来观察调用顺序,我们知道useEffect 是副作用,也就是函数组件调用完成的时候去回调安排副作用。
当依赖项为空的时候,是第一次加载完成的时候调用,如果有依赖项,则依赖出发页面改变的时候去调用。
关于retrun 通常useEffect 可以写return 闭包,那么这个return 的调用时机是什么呢?
带着这些问题,我们用初次加载(useEffect 依赖为空数组)、监听属性(props)、监听state 分别进行测试。
第一次加载也就是监听这部分逻辑,这个用hooks 的都知道就不多说。
useEffect(() => {
console.log('初始化加载');
return () => {
console.log('退出卸载');
};
}, []);
项目运行打印结果:
一切在估计中,继续。
卸载应用,验证retrun 是不是卸载时候调用,我们利用父视图显示卸载子组件。
点击卸载子组件,触发打印,一切正常。
我们用useEffect 监听props 属性的变化时机,return 是什么时候调用。
useEffect(() => {
console.log(`${title}渲染完成之后`);
return () => {
console.log(`清除上次${title}渲染`);
};
}, [title]);
项目运行打印结果:
页面渲染完之后,同样执行了监听的props 。 那么props 的return 是什么时候调用呢?
刷新title 属性试试,重外面更新title 属性,结果如下:
会先执行return 把上次的props 属性卸载掉,里面的props 值还是上次的。然后再执行进入。
useEffect(() => {
console.log(`${subTitle}渲染完成之后`);
return () => {
console.log(`清除上次${subTitle}渲染`);
};
}, [subTitle]);
一如既往,useEffect 初始化的时候会这么调用。
那么return 是怎么运行的呢?我们改变state 试一试,同样会先执行return 然后再进入。
跑第一遍:
先执行初始化,然后state 最后props 。 真的是这样吗?换个顺序试试
换个顺序再来一遍:
wtf 顺序改变了。
结论:
进入的加载顺序和执行顺序有关,卸载之后同样。