useState
const [state, setState] = useState(initialState);
1
state
在初始渲染期间,返回的状态 (state) 与传入的第一个参数 (initialState) 值相同。
更新 state
// 直接更新
setState(newState);
// 函数式更新
setState(prevCount => prevCount - 1);
1
2
3
4
5
2
3
4
5
惰性初始化 state
initialState 参数只会在组件的初始渲染中起作用,后续渲染时会被忽略。如果初始 state 需要通过复杂计算获得,则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用:
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});
1
2
3
4
2
3
4
第一个常见的使用场景是当创建初始 state 很昂贵时:
function Table(props) {
// ⚠️ createRows() 每次渲染都会被调用
const [rows, setRows] = useState(createRows(props.count));
// ...
}
1
2
3
4
5
2
3
4
5
为避免重新创建被忽略的初始 state,我们可以传一个 函数 给 useState:
function Table(props) {
// ✅ createRows() 只会被调用一次
const [rows, setRows] = useState(() => createRows(props.count));
// ...
}
1
2
3
4
5
2
3
4
5
FAQ
我应该使用单个还是多个 state 变量?
推荐把 state 切分成多个 state 变量,每个变量包含的不同值会在同时发生变化。
如果你之前用过 class,你或许会试图总是在一次 useState() 调用中传入一个包含了所有 state 的对象。
const [state, setState] = useState({ left: 0, top: 0, width: 0, height: 0 });
useEffect(() => {
function handleWindowMouseMove(e) {
// 展开 「...state」 以确保我们没有 「丢失」 width 和 height
setState(state => ({ ...state, left: e.pageX, top: e.pageY }));
}
// 注意:这是个简化版的实现
window.addEventListener("mousemove", handleWindowMouseMove);
return () => window.removeEventListener("mousemove", handleWindowMouseMove);
}, []);
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
组件的 state 拆分为 position 和 size 两个对象,并永远以非合并的方式去替换 position
const [position, setPosition] = useState({ left: 0, top: 0 });
const [size, setSize] = useState({ width: 100, height: 100 });
useEffect(() => {
function handleWindowMouseMove(e) {
setPosition({ left: e.pageX, top: e.pageY });
}
1
2
3
4
5
6
7
2
3
4
5
6
7
把独立的 state 变量拆分开还有另外的好处。这使得后期把一些相关的逻辑抽取到一个自定义 Hook 变得容
function Box() {
const position = useWindowPosition();
const [size, setSize] = useState({ width: 100, height: 100 });
// ...
}
function useWindowPosition() {
const [position, setPosition] = useState({ left: 0, top: 0 });
useEffect(() => {
// ...
}, []);
return position;
}
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