React - 使用 useState Hook
useState 是一个基础的 React hook,它允许 function component 维护自己的 state,并根据 state 变化重新渲染自身。useState 的签名如下 −
const [ <state>, <setState> ] = useState( <initialValue> )
其中,
initialValue − state 的初始值。state 可以是任意类型(number、string、array 和 object)。
state − 用于表示 state 值的变量。
setState − 用于表示由 useState 返回的更新 state 函数的函数变量。
setState 函数的签名如下 −
setState( <valueToBeUpdated> )
其中,valueToBeUpdated 是 state 要更新的值。设置和更新用户名的示例用法如下 −
// 初始化 state
const [name, setName] = useState('John')
// 更新 state
setName('Peter)
特性
useState 的显著特性如下 −
函数参数 − 它接受一个函数(返回初始 state)而不是初始值,并且仅在组件的初始渲染期间执行该函数一次。如果初始值的计算代价昂贵,这将有助于提升性能。
const [val, setVal] = useState(() => {
var initialValue = null
// 初始值的昂贵计算
return initialValue
})
验证先前值 − 它检查 state 的当前值和先前值,只有当它们不同时,React 才会渲染其子组件并触发 effects。这将提升渲染性能。
// ...
setName('John') // 更新 state 并重新渲染组件
// ...
// ...
setName('John') // 不会触发子组件的渲染,因为 state 的值未发生变化
// ...
批量处理多个 state 更新 − 多个 state 更新会被 React 内部批量处理并执行。如果需要立即执行多个 state 更新,可以使用 React 提供的特殊函数 flushSync,它会立即刷新所有 state 变化。
flushSync(() => setName('Peter'))
应用 state hook
让我们创建一个登录表单组件,并使用 useState hook 来维护表单的值。
首先,使用以下命令创建并启动 React 应用,
create-react-app myapp cd myapp npm start
接下来,在组件文件夹(src/components/LoginForm.js)下创建一个 React 组件 LoginForm
import { useState } from 'react';
export default function LoginForm() {
// render code
}
接下来,使用 useState hook 创建两个状态变量 username 和 password,如下所示 −
import { useState } from 'react';
export default function LoginForm() {
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
// render code
}
接下来,创建一个函数来验证登录数据,如下所示 −
import { useState } from 'react';
export default function LoginForm() {
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
let isEmpty = (val) => {
if(val == null || val == '') {
return true;
} else {
return false;
}
}
let validate = (e) => {
e.preventDefault()
if(!isEmpty(username) && !isEmpty(password)) {
alert(JSON.stringify({
username: username,
password: password
}))
} else {
alert("Please enter username / password")
}
}
// render code
}
这里,isEmpty 是一个用于检查数据是否可用或为空的函数。
接下来,渲染一个带有两个输入字段的登录表单,并使用状态变量(username 和 password)、状态更新方法(setUsername 和 setPassword)以及 validate 方法来处理表单。
import { useState } from 'react';
export default function LoginForm() {
return (
<div style={{ textAlign: "center", padding: "5px" }}>
<form name="loginForm">
<label for="username">Username: </label>
<input id="username" name="username" type="text"
value={username}
onChange={(e) => setUsername(e.target.value)} />
<br />
<label for="password">Password: </label>
<input id="password" name="password" type="password"
value={password}
onChange={(e) => setPassword(e.target.value)} />
<br />
<button type="submit" onClick={(e) => validate(e)}>Submit</button>
</form>
</div>
)
}
这里,
onChange 使用 hook 返回的状态设置函数。
onClick 使用 validate 函数来验证并显示用户输入的数据。
LoginForm.js
LoginForm 组件的完整代码如下 −
import { useState } from 'react';
export default function LoginForm() {
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
let isEmpty = (val) => {
if(val == null || val == '') {
return true;
} else {
return false;
}
}
let validate = (e) => {
e.preventDefault()
if(!isEmpty(username) && !isEmpty(password)) {
alert(JSON.stringify({
username: username,
password: password
}))
} else {
alert("Please enter username / password")
}
}
return (
<div style={{ textAlign: "center", padding: "5px" }}>
<form name="loginForm">
<label for="username">Username: </label>
<input id="username" name="username" type="text"
value={username}
onChange={(e) => setUsername(e.target.value)} />
<br />
<label for="password">Password: </label>
<input id="password" name="password" type="password"
value={password}
onChange={(e) => setPassword(e.target.value)} />
<br />
<button type="submit" onClick={(e) => validate(e)}>Submit</button>
</form>
</div>
)
}
App.js
接下来,将根应用组件 App.js 更新如下,
import './App.css';
import HelloWorld from './components/HelloWorld';
import LoginForm from './components/LoginForm';
function App() {
return (
<LoginForm />
);
}
export default App;
接下来,打开浏览器并检查应用。应用将使用状态变量收集用户输入的数据,并使用 validate 函数验证它。如果用户输入正确的数据,将显示如下所示的数据 −
否则,将抛出如下所示的错误 −

对象作为 state
在基于 class 的状态管理中,setState 方法支持状态对象的部分更新。例如,我们考虑登录表单数据以对象形式保存在 state 中。
{
username: 'John',
password: 'secret'
}
使用 setState 更新 username 只会更新状态对象中的 username,并保留 password 字段。
this.setState({
username: 'Peter'
})
在 hooks 中,setData(由 useState 返回的函数)会更新整个对象,如下面所示 −
// 创建 state
const [data, setDate] = useState({
username: 'John',
password: 'secret'
})
// 更新 state - 错误
setData({
username: 'Peter'
})
更新后的状态没有 password 字段,如下面所示 −
{
username: 'Peter'
}
为了修复这个问题,我们可以使用 JavaScript 中的扩展运算符,如下面所示 −
setData({
...data,
username: 'Peter'
})
让我们通过转换我们的 LoginForm 组件来创建一个新组件,并使用对象状态变量,如下面所示 −
import { useState } from 'react';
export default function LoginFormObject() {
const [data, setData] = useState({})
let isEmpty = (val) => {
if(val == null || val == '') {
return true;
} else {
return false;
}
}
let validate = (e) => {
e.preventDefault()
if(!isEmpty(data.username) && !isEmpty(data.password)) {
alert(JSON.stringify(data))
} else {
alert("Please enter username / password")
}
}
return (
<div style={{ textAlign: "center", padding: "5px" }}>
<form name="loginForm">
<label for="username">Username: </label>
<input id="username" name="username" type="text"
value={data.username}
onChange={(e) => setData( {...data, username: e.target.value} )} />
<br />
<label for="password">Password: </label>
<input id="password" name="password" type="password"
value={data.password}
onChange={(e) => setData({...data, password: e.target.value})} />
<br />
<button type="submit" onClick={(e) => validate(e)}>Submit</button>
</form>
</div>
)
}
在这里,
State 以对象(data)形式维护。
setData 由 useState hook 返回,用作状态更新函数。
data.* 语法用于获取状态的详细信息。
data 扩展运算符与 setData 函数一起使用来更新状态。
总结
useState hook 是函数组件中进行状态管理的一种简单易用的方式。useState 可以用于处理状态中的单个值或多个值。它支持基本数据类型和复杂对象。它允许多个状态设置函数(set*)并在内部进行批处理以简化流程。由于引入了 useState hook,函数组件终于得到了改进,可以实现任何功能(从无状态到有状态)。