在编写页面时,我们需要将后端传过来的数据显示到指定页面上。自然而然我们可以想到可以用多个变量保存数据,然后放到页面指定位置显示数据。那问题来了,这些“状态”变量值保存呢?

有的小伙伴可能会说,可以用 var 、const 、let 来定义变量。是的,它们确实可以定义变量,但是随之又会有新的问题出现—变量改变后,我们想让页面重新渲染,显然它们是做不到的!

所以这次”主角”— useState 出现了。

我可以简单认为 useState 可以定义变量,且这些变量改变后,页面也会随之重新渲染。

好了,进入正题。

1、useState 返回值

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

useState 返回一个长度为2的数组。大部分我们是这样定义属性值变量的。如下

  1. const [key, setKey] = useState(0);

useState 既然返回的是一个数组,我们可以这样写:

  1. const keyArr = useState(0);
  2. const key = keyArr[0]; // 变量
  3. const setKey = keyArr[1]; // 改变变量的函数

这种写法显然太麻烦了,所以也很少有人用。之所以聊这中写法,其实想让小伙伴理解 useState 的返回值的类型。
如果还有对 ES6 解构赋值不太了解的小伙伴,可以去 传送门

2、useState 用法

举例说明

  1. import { useState } from 'react';
  2. const Counter = () => {
  3. const [count, setCount] = useState(0);
  4. // 方法一
  5. function handleClick() {
  6. setCount(count + 1);
  7. }
  8. // 方法二
  9. function handleClickFn() {
  10. setCount((prevCount) => {
  11. return prevCount + 1;
  12. });
  13. }
  14. return (
  15. <>
  16. Count: {count}
  17. <button onClick={handleClick}>+</button>
  18. <button onClick={handleClickFn}>+</button>
  19. </>
  20. );
  21. }
  22. export default Counter;

我们通过 useState 定义了一个状态值变量:count,其初始值为:0
同时,还定义了一个用于改变 count 变量的函数:setCount

假如,我们想利用先前的 count 来计算(count + 1)新的 count 值,我们有两种方法实现,如上。
正常情况下,这两种方法没什么区别,都能改变变量的值,让页面重新渲染。

其实,这两种方法还是有点区别的,这也是前端面试经常问到的。如下

import { useState } from 'react';

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

        // 方法一
    function handleClick() {
        setCount(count + 1);
        setCount(count + 1);
    }

      // 方法二
    function handleClickFn() {
        setCount((prevCount) => {
            return prevCount + 1;
        });
        setCount((prevCount) => {
            return prevCount + 1;
        });
    }
    return (
        <>
            Count: {count}
            <button onClick={handleClick}>Click</button>
            <button onClick={handleClickFn}>ClickFn</button>
        </>
    );
}

export default Counter;

当我们点击 Click 按钮时,发现 count 每次只增加 1;
当我们点击 ClickFn 按钮时,发现 count 每次却增加 2;

这是因为当我们使用一个新的 state 更新 count 值,如果多次频繁调用 useState,react 为了提高性能,它将多次 setCount 合并为最后一次调用。所以,点击 Click 按钮时,count 每次只增加 1;

当我们使用函数式方法更新 state时,因为形参 preCount 永远拿到的都是最新的 count 值,而且它也不会将多次 setCount 合并调用。所以,点击 ClickFn 按钮时,count 每次增加 2;

如果你想定义多个变量,可以:

const [count, setCount] = useState(0);
const [value, setValue] = useState("你好,世界!");
const [visible, setVisible] = useState(false);

如果变量过多,我们也可以自定义 Hook — useSetState

/**
 * 自定义Hook--useSetState
 * @description 用法和Class组件的setState()类似
 */
import { useCallback, useState } from 'react';
const useSetState = (
  initialState = {},
)=> {
  const [state, setState] = useState(initialState);
  const setMergeState = useCallback((patch) => {
    setState((prevState) => ({ 
      ...prevState,
      ...(typeof patch === "function" ? patch(prevState) : patch),
    }));
  }, []);
  return [state, setMergeState];
};
export default useSetState;

3、补充

我们知道 useState 的形参 initialState 可以是的 字符串、数组、布尔值…类型,当它是一个函数时,在每次状态值或者属性值发生改变时,函数里的代码是否也会重新执行?

const [state, setState] = useState(initialState);
const Counter = () => {
    const [count, setCount] = useState(() => {
        const obj = JSON.parse(bigJsonData); // 昂贵操作
        return obj.count;
    });

    ...
}

答案是肯定不会重新执行的。

每当 React 重新渲染组件时,都会执行useState(initialState)。如果初始状态是(数字,布尔值等),则不会有性能问题。
当初始状态需要昂贵的性能方面的操作时,可以通过为useState(functionCallback)提供一个函数来返回初始值,functionCallback 仅会在初次渲染时执行,在后续的渲染将不会再执行。