Appearance
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
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
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
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
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
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
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
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
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
2
3
4
5
6
7
8
9
10
11