React useState 怎么用?

文章导读
Previous Quiz Next useState 是一个基础的 React hook,它允许 function component 维护自己的 state,并根据 state 变化重新渲染自身。useState 的签名如下 −
📋 目录
  1. 特性
  2. 应用 state hook
  3. 对象作为 state
A A

React - 使用 useState Hook



Previous
Quiz
Next

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 是一个用于检查数据是否可用或为空的函数。

接下来,渲染一个带有两个输入字段的登录表单,并使用状态变量(usernamepassword)、状态更新方法(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 函数验证它。如果用户输入正确的数据,将显示如下所示的数据 −

Applying State Hook

否则,将抛出如下所示的错误 −

Applying State Hook

对象作为 state

在基于 class 的状态管理中,setState 方法支持状态对象的部分更新。例如,我们考虑登录表单数据以对象形式保存在 state 中。

Applying State Hook
{
   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,函数组件终于得到了改进,可以实现任何功能(从无状态到有状态)。