React Hooks 发布赋予了函数组件可以使用状态和副作用的能力。 在众多Hooks中,React 中有两个用于现代状态管理的Hooks:useState 和 useReducer。 本教程逐步介绍 React 中的 useState , 并通过大量示例来帮助您开始使用 React Hook 进行状态管理。
SIMPLE STATE IN REACT
在过去,函数组件中不能使用状态。 因此,它们称为无状态组件。 然而,随着 React Hooks 的发布,状态也可以应用在这类组件当中,因此它们被 React 社区重新命名为函数组件。 以下代码演示了如何在具有 useState 钩子的函数组件中使用状态的简单示例:
const App = () => {
const [count, setCount] = React.useState(0);
const handleIncrease = () => {
setCount(count + 1);
};
const handleDecrease = () => {
setCount(count - 1);
};
return (
<div>
Count: {count}
<hr />
<div>
<button type="button" onClick={handleIncrease}>
Increase
</button>
<button type="button" onClick={handleDecrease}>
Decrease
</button>
</div>
</div>
);
};
这是一个简单的计数demo, useState 函数将初始状态的值作为参数。 在这种情况下,计数从 0 开始。此外,钩子返回一个包含两个值的数组:count 和 setCount。 它们是从允许重命名的返回数组中解构的。
第一个值是count,表示当前值。第二个值是一个函数,用于在调用它时使用传递给该函数的任何内容更新状态。 该函数也称为状态更新函数。 每次调用此函数时,React 都会重新渲染组件以渲染最近的状态。
这就是开始在 React 中进行简单状态管理所需的一切。 如果您对 React 的 useState 的一些使用注意事项感兴趣,请继续阅读。
COMPLEX STATE IN REACT
到目前为止,该示例仅显示了带有 JavaScript 原始类型的 useState。 这就是 useState 闪耀的地方。 它可用于整数、布尔值、字符串和数组。 但是,一旦您计划使用对象或更复杂的数组来管理更复杂的状态,您应该查看 React 的 useReducer 钩子。 useReducer 优于 useState 的场景有很多:
- 复杂状态容器
- 复杂的状态转换
- 有条件的状态更新 它还有助于通过仅使用 useState 来避免多次连续的状态更新。 如果你想在 React 中管理更复杂的状态,你绝对应该尝试使用 useReducer。
ASYNCHRONOUS STATE IN REACT
如果您依赖于实际状态来更新状态,会发生什么? 让我们通过一个例子来说明这种情况,我们使用 JavaScript 内置的 setTimeout 函数延迟状态更新:
const App = () => {
const [count, setCount] = React.useState(0);
const handleIncrease = () => {
setTimeout(() => setCount(count + 1), 1000);
};
const handleDecrease = () => {
setTimeout(() => setCount(count - 1), 1000);
};
return (
<div>
Count: {count}
<hr />
<div>
<button type="button" onClick={handleIncrease}>
Increase
</button>
<button type="button" onClick={handleDecrease}>
Decrease
</button>
</div>
</div>
);
};
每次单击其中一个按钮时,都会延迟一秒调用状态更新函数。 这适用于单击。 但是,请尝试连续多次单击其中一个按钮。 状态更新函数将始终在这一秒内在相同的状态(此处:计数)上运行。 为了解决这个问题,你可以从 useState 传递一个函数给状态更新函数以获取最新变更的状态:
import React from 'react';
const App = () => {
const [count, setCount] = React.useState(0);
const handleIncrease = () => {
setTimeout(() => setCount(state => state + 1), 1000);
};
const handleDecrease = () => {
setTimeout(() => setCount(state => state - 1), 1000);
};
return (
<div>
Count: {count}
<hr />
<div>
<button type="button" onClick={handleIncrease}>
Increase
</button>
<button type="button" onClick={handleDecrease}>
Decrease
</button>
</div>
</div>
);
};
export default App;
该函数为您提供执行该函数时的状态。 这样,您永远不会在任何陈旧状态下进行操作。 因此,一个好的经验法则可能是:如果您的状态更新取决于您之前的状态,则始终使用 useState 的更新函数中的函数。
React 的 useState 是管理状态的首选钩子。 它可以与 useReducer 和 useContext 一起用于 React 中的现代状态管理。 与 useReducer 相比,它是更轻量级的状态管理方法。
After reading [Translator's Note]
useState赋予初始值
如果是原始类型的值,那么直接赋值,如果是引用类型的值,存在一些差异,数组和对象直接赋值,但是函数需要执行,如下代码age为{name:'name'},name为undefined:
const App = () => {
const bar = () => {
return {name:'name'}
}
const foo = () => {
console.log('render');
}
const [age, setAge] = useState(bar);
const [name, setName] = useState(asd);
return (
<div>
Name: {name}
</div>
);
};
export default App
useState对于非函数的声明类型,赋初值是一个同步操作,那么如果赋初值的函数是一个异步操作那么结果会如何呢?看下面的代码:
import React, {
useState,
useLayoutEffect,
useEffect
} from 'react';
const App = () => {
const bar = async () => {
const res = await fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/relations.json')
console.log(5,res)
return res
}
const foo = () => {
console.log(2,'render');
}
const [age, setAge] = useState(bar);
console.log(1,age)
const [name, setName] = useState(foo);
console.log(3,age)
useEffect(() => {
console.log(4,age)
})
return (
<div>
Name: {name}
</div>
);
};
export default App
其结果是:
1 Promise {<pending>}
index.jsx:14 2 "render"
index.jsx:20 3 Promise {<pending>}
index.jsx:23 4 Promise {<pending>}
index.jsx:10 5 Response {type: "cors", url: "https://gw.alipayobjects.com/os/antvdemo/assets/data/relations.json", redirected: false, status: 200, ok: true, …}
日志按照顺序执行,直接赋给的是Promise {}对象,而不是异步后的结果,说明没有阻塞js执行和UI渲染。