持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第20天,点击查看活动详情
当数据被操作转换成UI时,中间会产生许多帮助过程,比如进行网络请求,比如事件绑定,比如使用原生dom...这些都是非核心逻辑;核心逻辑是jsx - element元素 - component - 虚拟dom - 真实dom
解决这些副作用的方案就是在组件中使用生命周期钩子函数,比如componentDidMount,componentDidUpdate,componentWillUnmount,分别是第一次挂载完,更新完,以及卸载之前的请求。
注意,render是纯函数,不要做副作用操作。
我们的hooks它的特点就是没有生命周期,却能做生命周期能干的事情。
比如说,点击按钮,改变数据:
<div>
<p>{state}</p>
<button onClick={()=>{
this.setState({count:this.state.count+1})
}}>click</button>
</div>
当页面第一次加载时,会调用componentDidMount,此后的每一次更新完毕都会调用componentDidUpdate去监听数据真的发生了变化。但如果使用hooks来监听。
function App(){
const [count,setCount] = useState(0)
//声明,告诉react这个环境有一个副作用要处理,具体行为,每次render后都会执行
//直接定义在函数里面,方便访问数据,props,state这两个
// 类似三个生命周期
useEffect(()=>{
//相当于挂载/更新
document.title= `you click ${count}`
})
return(
<div>
<p>{count}</p>
<button onClick={()=>{
setCount(count+1)
}}>click</button>
</div>
)
}
使用useEffect钩子函数的时候就会默认做在类组件中那两个生命周期应该做的事。而且写起来也简单,还在render内部写,方便访问数据。
依赖问题👇
但useEffect也有缺点,当管理的状态/数据变得多起来时,它无论哪一个发生了变化,都会跟着更新,浪费性能。比如有两个状态正在监听,其中count发生改变的时候,及时name还没有发生改变,可是还是会改变,会被渲染,所以就有些依赖问题。
为了解决这一问题,我们在钩子函数中设置一个依赖数组,当依赖数组里面存在的依赖项发生改变时才会触发该钩子函数:
// function App(){
const [count,setCount] = useState(0)
const [name,setName] = useState("lily")
//声明,告诉react这个环境有一个副作用要处理,具体行为,每次render后都会执行
//直接定义在函数里面,方便访问数据,props,state这两个
//类似三个生命周期
console.log("render");
//依赖数组,只有一个依赖项,只有这个依赖项发生改变才能执行副作用函数
useEffect(()=>{
//相当于挂载/更新
document.title= `you click ${count}`
},[count])//依赖于count,如果是【】空数组,只执行一次:类似于componentDidMount,适合做事件初始化,数组是多项的话,数组中任何一项改变都会触发useEffct副作用函数执行
return(
<div>
<p>{count}</p>
<p>{name}</p>
<button onClick={()=>{setCount(count+1)}}>setCount me</button>
<button onClick={()=>{setName("kaka")}}>setName me</button>
</div>
)
}
当数组中,为一个状态时,只有这个依赖项发生变化才能执行副作用函数;当[]为空数组,相当于componentDidMount,指挥初始执行一次,适合做初始化事件;如果数组是多项的话,数组中任何一个改变都会触发useEffect中的副作用函数。
资源释放👇
场景:设置一个时间变量在页面上显示,在类组件中使用定时器1000ms去改变定时器,使之活灵活现,当点击按钮时会触发卸载动作,将整个app卸载掉。如果在卸载之前没有将定时器清除的话大家都知道,定时器会一直存在,并且每1000ms还会执行。所以要用到生命周期钩子函数:componentWillUnmount,在卸载组件之前清除定时器,不清楚就会内存溢出。
说到底,上面那种导致类组件中定时器一直执行,除非卸载否则就会内存溢出的解决方法直击就是使render每隔一秒就会改变的同时,只将定时器执行一次,就可以做到卸载之后定时器不会再执行了。
也就是说,只渲染一次。
结合上一个方法,当后面的依赖数组为空时,就相当于componentDidMount钩子函数,使其只在页面加载完执行一次。
function App(props){
const [time,setTime] = useState(new Date().toLocalTimeString())
console.log("render");
useEffect(()=>{
timeID = setInterval(() => {
setTime(new Date().toLocalTimeString())
}, 1000);
},[]) //注意看,这里是空数组
return(
<div>
{this.state.time}
<button onClick={()=>{ ReactDOM.unmountComponentAtNode(document.getElementById("app"))
}}>卸载</button>
</div>
)
}