初入React——useState()

145 阅读4分钟

前言:前端小白一枚,入行前端不到一年,热衷于技术,所以写一些文章记录下自己的学习的历程。 对于刚入门React的我,useState无疑是接触最多的一个hooks,下面介绍下我目前学习到的知识

基础介绍

  • 它返回一个状态和一个修改状态的方法,状态需要通过这个方法来进行修改;
  • initialCount 是我们传入的一个初始状态,它是惰性的,我们可以通过传一个函数来返回一个值当作初始状态,并且这个函数只会在初始渲染时执行一次;

const [state, setState] = useState(initialState);

写法介绍

官网中有介绍到:我们用方括号定义了一个 state 变量。

const [count, setCount] = useState(0);

当我们使用useState定义state 变量时候,它返回一个有两个值的数组。第一个值是当前的 state,第二个值是更新 state 的函数。使用 [0] 和 [1] 来访问有点令人困惑,这是js的数组结构,那为什么不用对象结构呢?

数组和对象解构的区别:

1、数组的元素是依次排序的,数组解构时变量的取值由数组元素决定,变量名称可以任意命名

2、对象的属性没有次序,解构时,变量名称必须和属性相同,才能取到正确的值。

3、因此,数组会更加灵活,可以任意命名和修改state

从上述区别可知:因为对象结构返回的是对象,在解构对象的时候必须要和 useState 内部实现返回的对象同名,想要使用多次的话,必须得设置别名才能使用返回值。如果useState 返回的是数组,那么使用者可以对数组中的元素命名,代码看起来也比较干净。

问题:更新后无法立马获取最新值

当我更新完状态后,我马上打印这个值,发现值并未改变,但是视图却更新了

const DrawCom = ()=>{
    const [state,setState] = useState(0)
    const btnClick = ()=>{
        setState(1)
        console.log('state',state)//打印出来的值仍然是0
    }
    return(
        <>
        <p>{state}</p>
        <Button onClick={btnClick}>Click</Button>
        </>
    )
}

然后我去查询资料,发现useState的更新是异步的,然后综合了一些文章,简单总结下更新过程:

  1. useState每次执行会返回一个新的state(简单类型的等值拷贝)
  2. setState会触发UI更新(重新render,执行函数组件)
  3. 由于UI更新是异步任务,所以setState也是一个异步过程

所以如果我们需要在更新状态完成的同时并拿取到当前的值,可以这样写

使用useState的返回值

    const btnClick = ()=>{
        setState(pre=>{
            pre+=1
            console.log('pre',pre)
            return pre
        })
    }

使用useEffect()函数来辅助

const DrawCom = ()=>{
    const [state,setState] = useState(0)
    /*
    useEffect用于处理组件中的effect,通常用于请求数据,事件处理,订阅等相关操作
    简单来说就是可以用来监听状态等,第一个参数是函数,第二参数为可选,
    为空的时候每次组件渲染都会执行一次,空数组时只执行一次。如果是变量,那么变量变化就会执行一次
    */
    useEffect(()=>{
        console.log(state)
    },[state])
    const btnClick = ()=>{
        setState(1)
    }
    return(
        <>
        <p>{state}</p>
        <Button onClick={btnClick}>Click</Button>
        </>
    )
}

使用自定义的hooks

这里的方法是某个大佬文章看来的,具体文章链接忘了,大佬的思路是封装为一个类似于Promise的链式函数

const DrawCom = () => {
    const useStateWithCall = (initValue) => {
        const ref = useRef(0)
        const callFRef = useRef()
        const setFuncRef = useRef()
        let [state, setState] = useState(initValue)
        if (!ref.current) {
            ref.current = 1;
            setFuncRef.current = (newData, callF) => {
                callFRef.current = callF
                setState(newData)
                return Promise.resolve(newData)
            }
        }
        return [state, setFuncRef.current]
    }
    
    const [state, setState] = useStateWithCall(0)

    const btnClick = () => {
        setState(1).then(val => {
            console.log('val',val)
        })
    }
    return (
        <>
            <p>{state}</p>
            <Button onClick={btnClick}>Click</Button>
        </>
    )
}

问题:多次更新只生效一次

const DrawCom = ()=>{
    const [state,setState] = useState(0)
    const btnClick = ()=>{
        setState(state+1)
        setState(state+1)
        console.log(state)
    }
    return(
        <>
        <p>{state}</p>
        <Button onClick={btnClick}>Click</Button>
        </>
    )
}

上述代码执行了两次加1的操作,但是视图还是只有一次加1的显示。造成这个原因是因为两次setState(n+1)时候,实际上形成了两个闭包,都保存了对此时state的状态(state=0)的引用。
在setN后:

  1. 先分别生成了两个新的n,数值上都等于state+1(即1),但彼此无关。
  2. 分别进行了render,而只有最新一次render有效,此次render引用了最后一次setN函数里生成的state。

解决办法其实很简单,利用函数,接收旧值,进行更新

 const btnClick = ()=>{
        setState(state=>state+1)
        setState(state=>state+1)
        //这里就很进行两次操作,视图也会+2
    }

总结:对于useSate的使用以及目前遇到问题就暂时这些,以后如果有其他还会慢慢补充。最后,前端小白加写作小白一枚,刚开始起步,如果哪里写得不对,哪里文笔表达有误,请各位大佬不吝赐教(* ̄︶ ̄)