React_useState | 青训营笔记

86 阅读2分钟
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最后的值为11const 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 // 返回的信息是我们要修改的状态值
        })
    }
}