Skip to content
On this page

useState

作用

在函数组件中使用状态,修改状态值可让函数组件更新,类似于类组件中的setState

语法

const [state, setState] = useState(initialState); 返回一个 state,以及更新 state 的函数

JavaScript
import React, { useState } from "react";
export default function Demo(props) {
    let [num, setNum] = useState(10);
    const handler = () => {
        setNum(num + 1);
    };
    return <div>
        <span>{num}</span>
        <button onClick={handler}>新增</button>
    </div>;
};
1
2
3
4
5
6
7
8
9
10
11

设计原理

函数组件的更新是让函数重新执行,也就是useState会被重新执行;那么它是如何确保每一次获取的是最新状态值,而不是传递的初始值呢?

JavaScript
// 函数组件每一次渲染/更新,都具备独立的闭包
import React, { useState } from "react";
export default function Demo(props) {
    let [num, setNum] = useState(10);
    const handler = () => {
        setNum(100);
        setTimeout(() => {
            console.log(num); //10
        }, 1000);
    };
    return <div>
        <span>{num}</span>
        <button onClick={handler}>新增</button>
    </div>;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

实现原理

JavaScript
var _state;
function useState(initialState) {
  _state = _state | initialState;
  function setState(state) {
    _state = state;
    //...重新渲染组件
  }
  return [_state, setState];
}
1
2
3
4
5
6
7
8
9

更新多状态

方案一: 类似于类组件中一样,让状态值是一个对象(包含需要的全部状态),每一次只修改其中的一个状态值!

问题:不能像类组件的setState函数一样,支持部分状态更新!

JavaScript
import React, { useState } from "react";
export default function Demo(props) {
    let [state, setState] = useState({
        x: 10,
        y: 20
    });
    const handler = () => {
        // setState({ x: 100 }); //state={x:100}
        setState({
            ...state,
            x: 100
        });
    };
    return <div>
        <span>{state.x}</span>
        <span>{state.y}</span>
        <button onClick={handler}>处理</button>
    </div>;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

方案二:执行多次useState,把不同状态分开进行管理「推荐方案」

JavaScript
import React, { useState } from "react";
export default function Demo(props) {
    let [x, setX] = useState(10),
        [y, setY] = useState(20);
    const handler = () => {
        setX(100);
    };
    return <div>
        <span>{x}</span>
        <span>{y}</span>
        <button onClick={handler}>处理</button>
    </div>;
};
1
2
3
4
5
6
7
8
9
10
11
12
13

更新队列机制

和类组件中的setState一样,每次更新状态值,也不是立即更新,而是加入到更新队列中!

React 18 全部采用批更新

React 16 部分批更新,放在其它的异步操作中,依然是同步操作!

可以基于flushSync刷新渲染队列

JavaScript
import React, { useState } from "react";
import { flushSync } from 'react-dom';

export default function Demo(props) {
    console.log('OK');
    let [x, setX] = useState(10);
    let [y, setY] = useState(20);

    const handler = () => {
        /* setX(100);
        setY(200); */

        /* setTimeout(() => {
            setX(100);
            setY(200);
        }, 1000); */

        /* flushSync(() => {
            setX(100);
        });
        setY(200); */
    };

    return <div>
        <span>{x}</span>
        <span>{y}</span>
        <button onClick={handler}>处理</button>
    </div>;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

函数式更新

如果新的 state 需要通过使用先前的 state 计算得出,那么可以将函数传递给 setState;该函数将接收先前的 state,并返回一个更新后的值!

JavaScript
import React, { useState } from "react";
export default function Demo() {
    let [num, setNum] = useState(10);
    const handler = () => {
        for (let i = 0; i < 10; i++) {
            // 函数式更新
            setNum(num => {
                return num + 1;
            });
        }
    };
    return <div>
        <span>{num}</span>
        <button onClick={handler}>处理</button>
    </div>;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

惰性初始state

如果初始 state 需要通过复杂计算获得,则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用!

JavaScript
import React, { useState } from "react";
export default function Demo(props) {
    let [num, setNum] = useState(() => {
        let { x, y } = props;
        return x + y;
    });
    return <div>
        <span>{num}</span>
    </div>;
};
1
2
3
4
5
6
7
8
9
10

性能优化

调用 State Hook 的更新函数,并传入当前的 state 时,React 将跳过组件的渲染(原因:React 使用 Object.is 比较算法,来比较新老 state;注意不是因为DOM-DIFF;)!

JavaScript
import React, { useState } from "react";
export default function Demo() {
    console.log('render');
    let [num, setNum] = useState(10);
    return <div>
        <span>{num}</span>
        <button onClick={() => {
            setNum(num);
        }}>处理</button>
    </div>;
};
1
2
3
4
5
6
7
8
9
10
11
沪ICP备20006251号-1