React官方简介:Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
本文中讲解的useState就是React中的其中一个Hook。
useState 通过在函数组件里调用它来满足给组件添加一些内部state(状态),调用useState会返回一个数组:当前状态和修改(更新)状态的函数,调用修改状态的函数来修改状态并触发视图的更新。
2.1 useState语法
const [state, setState] = useState(initialValue);
- state: 用来存储状态的值;
- setState:修改状态的函数;
- initialValue:函数式组件第一次渲染时state的初始值。
下面我们通过一个简单的例子简单讲解下useState的用法:
import { useState } from "react";const Demo = () => {const [num, setNum] = useState(0);const handle = () => {setNum(num + 1);};return (<>{num}>);
};
export default Demo;
分析Demo初始化到点击按钮修改num值后的重新渲染过程
1. 进入页面会自动进行第一次渲染组件
2. 点击新增按钮,执行handle方法
3. 组件重新渲染
函数组件的每一次渲染(或者是更新),都是把函数(重新)执行,产生一个全新的“私有上下文”!
- 内部的代码也需要重新执行
- 涉及的函数需要重新的构建{这些函数的作用域(函数执行的上级上下文),是每一次执行Demo函数产生的闭包}
- 每一次执行Demo函数,也会把useState重新执行,但是:
- 执行useState,只有第一次,设置的初始值会生效,其余以后再执行,获取的状态都是最新的状态值而不是初始值
- 返回的修改状态的方法,每一次都是返回一个新的
2.2 useState异步更新
先来看一个例子:
import { useState } from "react";const Demo = () => {console.log('RENDER渲染');const [x, setX] = useState(10);const [y, setY] = useState(20);const [z, setZ] = useState(30);const handle = () => {setX(x+ 1);setY(y+ 1);setZ(z+ 1);};return (<>>);
};
export default Demo;
在点击按钮后,'RENDER渲染’会输出几次?
答案是:1次。
执行handle函数时,会将所有的关于修改状态的函数放入更新队列中,最后一起重新渲染视图。
2.3 useState自带性能优化机制
useState自带了能优化的机制:
- 每一次修改状态值的时候,会拿最新要修改的值和之前的状态值做比较(基于Object.is做比较);
- 如果发现两次的值是一样的,则不会修改状态,也不会让视图更新。
示例如下:
import { useState } from "react";const Demo = () => {console.log('RENDER渲染');const [x, setX] = useState(10);const handle = () => {for (let i = 0; i < 10; i++) {setX(x + 1);}};return (<>>);
};
export default Demo;
上述代码,点击按钮后,'RENDER渲染’只输入一次,在循环的过程中,setX(x + 1) 中的x的值访问的一直是handle函数的上级上下文的x,所以每次x都是10,也就是说每一次执行setX,x的值都为11,react内部优化机制就会通过比较值是否更改来决定视图是否重新渲染。
2.4 useState惰性化处理
我们来看一段代码
import { useState } from "react";const Demo = (props) => {let { x, y } = props; // 假设父组件传了x 和 y两个类型为number的数据let total = 0;for (let i = x; i <= y; i++) {total += i;}const [num, setNum] = useState(total);const handle = () => {setNum(1);};return (<>>);
};
export default Demo;
上述代码num的初始值我们需要把基于属性传递进来的x/y,经过其他处理的结果作为初始值,但是num只有函数组件第一次执行的时候才会用到total,页面每次渲染都会重新执行for循环,就会造成资源浪费,这时我们就可以使用useState的第二种写法。
let [num, setNum] = useState(() => {let { x, y } = props; // 假设父组件传了x 和 y两个类型为number的数据let total = 0;for (let i = x; i <= y; i++) {total += i;}return total;
});
上述代码就是useState的惰性化处理。
2.5 useState 修改函数状态的第二种写法
const [state, setState] = useState((prev) => prev + 1)
prev:存储上一次的状态值
return prev + 1:返回要修改为的状态值。
上一篇:AOP面向切面编程思想。