今天简单来聊一下我们经常在React的函数组中出镜率最高的两个Hook API-useState&useEffect。虽然一直在用但是可能并不知道它们的具体执行流程和使用细节,今天就让我们一起来捋一捋
State Hook
state Hook,就是一个在函数组件中使用状态的方法(useState)
useState
- 参数: 为状态的默认值
- 返回值:
- 获取到的状态值
- 设置该状态的函数
function App (props) {
const [n, setN] = useState(0)
const [m, setM] = useState(true)
return (
<div>
这是一个函数组件
</div>
)
}
一个函数组件中,可以有多个状态(多次使用useState)
基本原理
- 当运行函数组件时,会生成一个组件节点,在组件节点生成的过程中,会创建真实的
DOM节点,同时也会做一些其他的事情,比如创建一个状态表格(类似与数组) - 第一次运行函数组件时(调用App函数),发现第一次使用
useState - 检测状态表格中是否含有下标为
0,状态值为默认值0的这一项 - 如果没有就新建一个这样的状态放进状态表格中,如果有就直接获取,
- 第二次发现使用
useState就重复第 3、 4步 - 当该函数组件重新渲染时,会再次寻找
useState,根据该出现的次数,对应状态表格中的数据进行查找
使用细节
useState尽量写在函数组件的函数体开始部分,便于阅读useState不要写在循环或者判断语句里(原因参考基本原理中的根据出现次数找下标对应状态值)useState返回的第二个值(setXXX)引用是不变的,目的是为了节约内存空间- 使用第二个返回值函数(
setXXX)改变数据,如果新的数据和旧的数据是完全一样的(Objec.is比较),那么组件不会重新渲染 - 使用第二个返回值函数改变数据,新值会直接替换掉旧值,而不会合并
- 如果要实现强制刷新组件
- 类组件:this.forceUpdate()
- 函数组件:给setN函数传入一个空对象
- 如果某些组件之间没有必然的联系,那么尽量写成多个状态的形式,不要合并成一个对象
- 和类组件的改变状态一样,函数组件改变状态时也有可能是异步的(dom事件中改变状态),那么此时就会将多次改变状态合并在一起最终变化,提高效率。此时是不能信任新的值,而是应该使用回调函数的形式
function App (props) {
const [n, setN] = useState(0)
return (
<div>
{n}
<div>
<button onClick={
() => {
setN(n + 1)
setN(n + 1)
// setN(preValue => preValue+1)
// setN(preValue => preValue+1)
}
}>n + 1</button>
</div>
</div>
)
}
上面的代码并不会因为,点击一次而直接+2.
因为在dom中状态的改变是异步的,就会将多个状态改变合并在一起,
那么当第一次setN(n + 1)调用之后,真正的状态值n并没有发生变化,依旧是0
第二次调用setN(n + 1)时,n值依旧是0,所以两个状态都完成之后,得到的值依然是1
注释部分为正确写法,通过回调函数的参数,来获取可信任的状态值
Effect Hook
useEffect
用于在函数组件中处理副作用
副作用:
- ajax请求
- 异步操作
- 定时器
- 本地存储
- 更改真实DOM对象
- 其他会对外部产生影响的操作
函数:useEffect,该函数接收两个参数
- 第一个参数接收一个函数,需要进行副作用操作的函数
- 数组,作为操作函数的依赖项
使用细节
- 副作用函数的运行节点:在页面完成真实的UI渲染之后, 因此它的执行是异步的,并且不会阻塞浏览器
- 每个函数组件中,可以有多个
useEffect函数 useEffect函数中可以有返回值,返回值如果是一个函数的话,则该函数为清理函数- 清理函数的运行节点是,在每次运行副作用函数之前
- 首次渲染组件不会运行清理函数
- 组件被销毁时一定会运行清理函数
useEffect的第二个参数- 必须是数组
- 数组中传递该副作用的依赖数据
- 当组件重新渲染后,只有依赖数据与上一次的不一样时,才会重新渲染
- 当传递的依赖数据始终不发生变化时
- 副作用函数只在第一次渲染之后运行
- 清理函数仅在卸载组件后运行
- 副作用函数中,如果使用了函数上下文中的变量,则由于闭包的影响,会导致副作用函数中变量不会实时变化。