React的Hooks之useState

88 阅读4分钟

UseState

是react中的一个Hook,它可以在函数组件中添加状态到函数组件;

const [state, setState] = useState(initValue);
  • useState函数接收一个初始化参数inititalState,其返回值使用数组解构出两个参数:state和setState。其中initValue是初始值,可以是基本类型,对象类型,或者可以为返回值的一个箭头函数;
  • 在初始化渲染期间,返回的状态与传入的第一个参数的值相同
  • setState函数用于更新state。他接收一个新的state值并将组件的一次渲染加入队列。
  • 在后续的重新渲染中,useState返回的第一个值将始终是更新后的最新的state。

useState有两种更新方式

useState的更新state分为两种,直接更新和函数式更新。

  • 直接更新setState(newState):直接传入新的state值即可,这种情况适用于返回的新值与旧值不存在依赖关系;另外,当使用整个对象作为传递给useState的的初始值,更新的时候它不会自动合并更新对象。
  • 函数式更新:这个新的state的值需要用一个函数来返回;preState总能拿到上一次更新成功之后的最新状态;setState((preState)=>{return nextState})
  • 区别:如果正常使用,这两种方式没啥区别,但是如果是异步更新,他们之间的差别就体现出来了
function Counter() {
  const [count, setCount] = useState(0);
  function handleClick() {
    setTimeout(() => {
      setCount(count + 1)
    }, 3000);
  }
  function handleClickFn() {
    setTimeout(() => {
      setCount((prevCount) => {
        return prevCount + 1
      })
    }, 3000);
  }
  return (
    <>
      Count: {count}
      <button onClick={handleClick}>+</button>
      <button onClick={handleClickFn}>+</button>
    </>
  );
}
  • 如果点击第一个按钮,即调用handleClick函数,点击按钮延迟3秒之后去调用setCount函数,当快速点击按钮时,也就是在3S内多次点击触发更新,但是只有一次生效,因为count的值是没有变化的;
  • 而当使用函数式更新state的时候,这种问题就没有了,因为它可以获取之前的state值,也就是代码中的prevCount每次都是最新的值。这是因为函数更新的时候,setCount函数将会被放在一个任务队列中,每一个setCount函数都可以拿到上一次更新成功后的状态值。
  • React中,当我们调用setXXX来更新状态时,React并不会立即更新状态值。相反,它将更新请求加入到一个队列中,并在稍后的时间点批量处理这些更新请求。这样做是为了优化性能,避免频繁更新导致的性能问题。 考虑以下代码示例:
const [count, setCount] = useState(0); 
function handleClick() {
setCount(count + 1); 
console.log(count); // 输出的还是旧的 count 值 
} 

在这个例子中,我们通过点击事件来更新count状态。如果我们直接传入一个新的值来更新状态,比如setCount(count + 1),此时的count还是旧的值,因为状态更新是异步的,还没有被React更新。 为了解决这个问题,React允许我们传入一个函数来更新状态,这个函数接收上一个状态值作为参数,并返回一个新的状态值。通过这种方式,我们可以确保在更新状态之前使用最新的状态值进行计算,避免出现不可预料的结果。 修正后的代码示例:

const [count, setCount] = useState(0);
function handleClick() {
setCount(prevCount => prevCount + 1); 
console.log(count); // 输出的是最新的 count 值 
} 

现在,setCount接收一个函数作为参数,并传入了prevCount,它是最新的状态值。 在这个函数内部,我们可以使用prevCount进行计算,并返回一个新的状态值。 这样可以确保我们在更新状态之前使用了最新的值。

注意

  • 如果你的更新函数的返回值与当前的state相同,则随后的渲染就会跳过;
  • hook中的useState与class中setState不同,useState不会自动合并更新对象,这里可以使用函数式的setState结合展开运算符达到合并的目的;
  • useReducer是另一种方案,它更适合用于管理包含多个子值的state对象
  • initValue是初始的state,可以是任意的数据类型,也可以是一个有返回值的回调函数(惰性化state)
const [state, setState] = useState(() => {
  const initialState = someExpensiveComputation(props);
  return initialState;
});
  • 在开发中,发现无论useState函数被调用了多少次,他们之间都是互相不影响的,都是相互独立的;是因为React根据useState的调用顺序来执行的,React的内部声明了一个数组,按照其useState函数调用的顺序,来保存其数据。来保存其数据。所以,一定要在函数的外层保存useState,不能在if else,for循环中,子函数中调用useState
  • React中通过类似单链表的形式来代替数组的。通过next按顺序串联所有的hook。