useState

139 阅读3分钟

useState这个hook主要用来获取和更新数据,useState 返回2个元素的数组

  • 第一个是 state 的当前值
  • 第二个是 state 的 setter 方法,调用时会触发 rerende

1.setState的常规使用

例如

import react, { useState } from 'react';
function hooker() {
    const [count, setCount] = useState(0); // 这里左边定义了一个变量和一个改变这个变量的函数
    // 右边通过这个hook钩子传入了一个初始值来赋值给这个变量
    return (
        <div>
           <button @click={() => {
               setCount(count+1)
           }}></button>
        </div>
    )
}

2.setState中的 previous state

使用场景:当我们目前更新的state需要依赖state的上一个状态值的时候,需要使用previous state,此时setCount状态更新函数中的参数不再是一个值,而是一个函数参数,这个函数参数中的入参就是previous state。

import react, { useState } from 'react';
function hooker() {
    const [count, setCount] = useState(0); // 这里左边定义了一个变量和一个改变这个变量的函数
    // 右边通过这个hook钩子传入了一个初始值来赋值给这个变量
    const addCount = () => {
        for(let i = 0; i< 5; i++) {
            setCount(count + 1)
        }
    }
    return (
        <div>
           <button @click={ addCount }>直接加5</button>
        </div>
    )
}

我们期望的结果是点击button,count可以直接加5,因为我们在for循环中执行了5次setcount(count+1),那么第一次执行的时候setcount(count + 1),此时count变为2。然后又执行一次setcount(count+1),那么count执行count = count + 1 => 2 + 1 = 3,所以这样执行下去,count值就被加1执行了5次。但是事实真的是这样吗? 真实执行结果是:点击button,count最后值变为了2。 为什么?因为setCount更新方法是异步的。(考点:useState导出的更新函数是同步的还是异步的)
这里需要解决两个问题

2.1 useState导出的更新函数是同步的还是异步的?

2.2 如何解决button执行五次+1的操作?

针对第二个问题:setCount((previous) => previous + 1),即setcount函数参数为一个执行函数,它的函数参数为previous。使用 previousState 时,要使用 setter function 的方式,传参给 setState 方法。这样就能来确保拿到的是准确的之前的previous state。
如下:

import react, { useState } from 'react';
function hooker() {
    const [count, setCount] = useState(0); // 这里左边定义了一个变量和一个改变这个变量的函数
    // 右边通过这个hook钩子传入了一个初始值来赋值给这个变量
    const addCount = () => {
        for(let i = 0; i< 5; i++) {
            setCount((previous) => previous + 1)
        }
    }
    return (
        <div>
           <button @click={ addCount }>直接加5</button>
        </div>
    )
}

3.useState钩子的注意点

3.1 useState中的state数据类型为对象或者数组的时候

(1)对象

这里我们想只更改对象的某个属性,而setName函数在传入更新对象参数的时候,需要传入整个参数,不能只传入某个更新值,这样会报错。所以需要在setName函数中需要将name进行解构赋值,放在一个新的对象中,再加入更新的属性即可。

import React, { useState } from 'react'

function HookCounter() {

  const [name, setName] = useState({
    firstName: '',
    lastName: ''
  })

  return (
    <form>
      <input
        type="text"
        value={name.firstName}
        onChange={e => {
          setName({
            firstName: e.target.value
          })
        }}
      />
      <input
        type="text"
        value={name.lastName}
        onChange={e => {
          setName({
            lastName: e.target.value
          })
        }}
      />
      <h2>Your first name is {name.firstName}</h2>
      <h2>Your last name is {name.lastName}</h2>
    </form>
  )
}

export default HookCounterimport React, { useState } from 'react'

所以正确更新对象的方式是:手动合并对象属性

import React, { useState } from 'react'

function HookCounter() {
  const [name, setName] = useState({
    firstName: '',
    lastName: ''
  })

  return (
    <form>
      <input
        type="text"
        value={name.firstName}
        onChange={e => {
          setName({
            ...name, // 将state进行解构赋值
            firstName: e.target.value // 新的属性会覆盖之前name中的对应属性
          })
        }}
      />
      <input
        type="text"
        value={name.lastName}
        onChange={e => {
          setName({
            ...name,
            lastName: e.target.value
          })
        }}
      />
      <h2>Your first name is {name.firstName}</h2>
      <h2>Your last name is {name.lastName}</h2>
      <h2>{JSON.stringify(name)}</h2>
    </form>
  )
}

export default HookCounter

(2)数组

import React, { useState } from 'react'

interface ItemType {
  id: number
  value: number
}

function UseStateWithArray() {
  const [items, setItems] = useState<ItemType[]>([])

  const addItem = () => {
    setItems([
      ...items, // 这里对数组进行解构赋值
      {         // 这里是进行相加的增加
        id: items.length,
        value: Math.ceil(Math.random() * 10)
      }
    ])
  }

  return (
    <div>
      <button onClick={addItem}>add a number</button>
      <ul>
        {
          items.length > 0 && items.map((item: ItemType) => (
            <li key={item.id}>{item.value}</li>
          ))
        }
      </ul>
    </div>
  )
}

export default UseStateWithArray