1.作用
2.State Hook
import React, {useState} from 'react';
const Admin = () =>{
const [count , setCount] = useState(0);
const [obj ,setObj] = useState({name:'zs'});
return (
<div>
<h2>{count}</h2>
<button onClick={()=>{setCount(count+1)}}> change </button>
<h2>{obj.name}</h2>
<button onClick={()=>{setObj({name:'l4'})}}> change </button>
// 不生效
<button onClick={()=>{setObj(Object.assign(obj,{name:'lll'}))}}> change </button>
// 这样添加属性
<button onClick={()=>{setObj({...obj,age:20})}}> change </button>
</div>
)
}
export default Admin;
3.Effect Hook
副作用操作:如 数据获取,设置定时器,更改DOM
useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。
常见副作用操作分为:需要清除的 和 不需要清除的
1.不需要清除的effect
比如:发送网络请求,手动更新DOM,记录日志等
一般在Class中,都会放到 componentDidMount 和 componentDidUpdate 中。
但很多时候,我们需要在组件加载和更新时进行同样的操作,所以在class中我们需要在这 两种声明周期 调用两次
在Effect hook中:
-
useEffect 做了什么?:定义组件需要在渲染后执行的操作,会自动在执行DOM更新后调用
-
为什么在组件内部调用 useEffect?:放在组件内部可以让我们可以直接访问组件内部变量(闭包机制)
-
useEffect 会在每次渲染后都执行吗?:会在 第一次渲染后 和 每次更新之后 都会执行,就不用去区分是挂载还是更新。每次运行effect时,DOM都已经更新完成。
2.需要清除的effect
比如:订阅外部数据源
需要进行清除,可以防止内存泄漏
在class中,会在 componentDidMount 中设置,在 componentWillUnmount 中清除
在effect hook中:
在effect中返回一个函数,react会在执行清除时调用它
-
为什么要在 effect 中返回一个函数?:这是effect可选的清除机制,每个effect可以返回一个清除函数,这样可以把添加和移除订阅的逻辑放在一起。
-
React 何时清除 effect?: react会在组件卸载的时候执行清除操作(每次渲染的时候都会执行,所以react会在执行下一个effect之前对上一个effect进行清除)
可以使用多个effect实现关注点分离:
可以把不相关的逻辑分离在不同的effect中,并且把相同的逻辑放在同一个effect中
为什么每次更新的时候都要运行 Effect?
effect的清除阶段在每次重新渲染时都会执行,而不是只在卸载组件的时候执行一次,
场景:
componentDidMount() {
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentDidUpdate(prevProps) {
// 取消订阅之前的 friend.id
ChatAPI.unsubscribeFromFriendStatus(
prevProps.friend.id,
this.handleStatusChange
);
// 订阅新的 friend.id
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentWillUnmount() {
ChatAPI.unsubscribeFromFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
当props中的id变化后需要在componentDidUpdate中对上一个进行清理,对下一个进行订阅。
而在Hook中还是:
useEffect(() => {
// ...
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
这样不需要特定的代码来处理更新逻辑,每次渲染后都会对前一个清理,后一个订阅。
通过跳过 Effect 进行性能优化?
每次渲染后都执行清理或者执行 effect 可能会导致性能问题。
在class中我们会通过在 componentDidUpdate 中对 prevProps或者prevState 进行比较来解决:
componentDidUpdate(prevProps, prevState) {
if (prevState.count !== this.state.count) {
document.title = `You clicked ${this.state.count} times`;
}
}
而在hook里面,如果某些特定值在两次渲染中没有变化,可以通知react跳过对effect的调用:使用 useEffect的第二个可选参数的数组
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时更新
当重新渲染时,count值不变,那么这个effect就被跳过了
如果想执行只运行一次的effect(只在组件挂载和卸载时执行),那么可以传递一个空数组[]作为第二个参数。即effect不依赖于props或者state中任何值,不需要重复执行