useState
useState: React Hook函数之一,目的是在函数组件中使用状态,并且后期基于状态的修改,可以让组件更新
let [num, setNum] = useState(initialValue)
-
执行useState,传递的initialValue是初始的状态
-
执行这个方法,返回结果是一个数组 [ 状态值,修改状态的方法]
- num变量存储的是:获取的状态值
- setNum变量存储的是:修改状态的方法
-
执行 setNum(value)
- 修改状态为value
- 通知试图更新
函数组件(或者Hooks组件)不是类组件,所以没有实例的概念(调用组件不再是创建类的实例,而是把函数执行,产生一个私有上下文),再所以,在函数组件中不涉及this的处理
const Demo = function Demo() {
let [num, setNum] = useState(0)
const handle = () => {
setNum(num + 10)
setTimeout(()=>{
console.log(num) // 0 由于闭包 函数执行后作用域是函数执行的上级上下文 此时num=0
})
}
return <div className="demo">
<span className="num">{num}</span>
<Button type="primary" size="small" onClick={handle}>新增</Button>
</div>
}
export default Demo
函数组件的每一次渲染(或者是更新),都是函数重新执行,产生一个全新的 “上下文“
-
内部代码也需要重新执行
-
涉及的函数需要重新构建(这些函数的作用域(函数执行的上级上下文),是每一次执行DEMO产生的闭包,所以每次更新后的函数和前一次的函数不相等)
-
每一次执行Demo函数,也会把useState重新执行
- 执行useState,只有第一次设置的初始值会生效,其余以后再执行,获取的状态都是最新的状态值(而不是初始值)
- 返回的修改状态的方法,每一次都是返回最新的
// 自己实现useState
var _state;
function useState(initialValue) {
if (typeof _state === 'undefined') {
if (typeof initialValue === 'function') {
_state = initialValue()
}else {
_state = initialValu
}
}
var setState = function setState(value) {
_state = value
// 通知视图更新
}
return [_state, setState]
}
当函数组件中有多个状态时,修改时要注意
let [state, setState] = useState({
supNum: 10,
oppNum: 5
})
setState({
...state,
supNum: state.supNum + 1
})
注意:
const handle = () => {
setX(x+1)
setY(y+1)
setZ(z+1)
}// 异步更新一次
const handle = () => {
setTimeout(() => {
setX(x+1)
setY(y+1)
setZ(z+1)
})
}// 同步更新三次
const handle = () => {
flushSync(() => {
setX(x+1)
setY(y+1)
})
}
setZ(z+1)
// 特殊处理 flushSync会刷新更新队列
// 更新两次
useState函数更新和优化机制
const Demo = function Demo() {
console.log('视图渲染');
let [x, setX] = useState(10)
const handle = () => {
for (let i = 0; i < 10; i++) {
setX(x + 1)
}
}
return <div className="demo">
<span className="num">{x}</span>
<Button type="primary" onClick={handle}>新增</Button>
</div>
}
export default Demo
// 视图更新一次 x最后的值为11
const handle = () => {
for (let i = 0; i < 10; i++) {
setX(10)
}
}
// 更新x的值为10 与原值一样 视图不会更新
原因:setX操作是异步的,将操作放到任务队列中,最后统一执行。每次执行setX(x + 1) 都将更新操作放到更新队列中 每一次的x都是上级上下文中的x:10,所以最后的x是11。
useState自带了性能优化的机制:
- 每一次修改状态值的时候,会拿最新要修改的值和之前的状态值做比较( 基于Object.is作比较)
- 如果发现两次的值是一样的,则不会改变状态,也不会让视图更新
如果想实现x值自增10次并且视图只更新一次
const handle = () => {
for (let i = 0; i < 10; i++) {
// prev存储的是上一次的状态值
setX((prev) => {
return prev + 1 // 返回的信息是我们要修改的状态值
})
}
}