到 React 16.8 目前为止,如果编写函数组件,然后遇到需要添加状态的情况,咱们就必须将组件转换为类组件。
编写
class Thing extends React.Component
,将函数体复制到
render()
方法中,修复缩进,最后添加需要的状态。
今天,可以使用 Hook 获得相同的功能,并为自己节省了工作时间。在本文中,主要介绍
useState
hook。
useState 做啥子的
useState
hook 允许咱们向函数组件添加状态,我们通常称这些为“ hooks”,但它们实际上是函数,与 React 16.8 捆绑在一起。 通过在函数组件中调用
useState
,就会创建一个单独的状态。
在类组件中,
state
总是一个对象,可以在该对象上添加保存属性。
对于 hooks,state 不必是对象,它可以是你想要的任何类型-数组、数字、布尔值、字符串等等。每次调用
useState
都会创建一个
state
块,其中包含一个值。
示例1:使用 useState 显示/隐藏组件
这个示例是一个组件,它显示一些文本,并在末尾显示一个
read more
链接,当单击链接时,它展开剩下的文本。
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
// 两个 props:
// text - 显示的内容
// maxLength - 在点击“read more”之前显示多少个字符
function LessText({ text, maxLength }) {
// 创建一个状态,并将其初始化为“true”
const [hidden, setHidden] = useState(true);
if (text <= maxLength) {
return <span>{text}</span>;
return (
{hidden ? `${text.substr(0, maxLength).trim()} ...` : text}
{hidden ? (
<a onClick={() => setHidden(false)}> read more</a>
) : (
<a onClick={() => setHidden(true)}> read less</a>
</span>
ReactDOM.render(
<LessText
text={`专注、努力是成功的真正关键。把你的眼睛盯在目标上,然后朝着目标迈出下一步`}
maxLength={35}
document.querySelector('#root')
仅用一行代码,我们就使这个函数组件有状态:
const [hidden, setHidden] = useState(true);
但是这个函数到底在做什么呢?如果每次渲染都调用它(确实如此),它又是如何保留状态的。
Hooks 实现的技巧
这里的“神奇”之处是,React在每个组件的幕后维护一个对象,并且在这个持久对象中,有一个“状态单元”数组。当你调用useState
时,React
将该状态存储在下一个可用的单元格中,并递增数组索引。
假设你的 hooks
总是以相同的顺序调用(如果遵循 hooks 的规则,它们将是相同的顺序),React能够查找特定useState
调用的前一个值。对useState
的第一个调用存储在第一个数组元素中,第二个调用存储在第二个元素中,依此类推。
这也不是很神奇的事情,主要它依赖于你可能没有想过的事实:咱们写的的组件是由React
调用 ,所以它可以在调用组件之前事先做好一些工作。 而且,渲染组件的行为不仅仅是函数调用。 像<Thing />
这样的JSX
被编译为React.createElement(Thing)
- 显然 React 可以控制它的调用方式和时间。
示例2:根据之前的状态更新状态
看看另一个例子:根据前一个值更新state
的值。
咱们要造个计步器,每点击一次按钮,就计一次,点击完后,它会告诉你你走了多少步。
import React, { useState } from 'react';
function StepTracker() {
const [steps, setSteps] = useState(0);
function increment() {
setSteps(steps => steps + 1);
return (
总共走了 {steps} 步!
<button onClick={increment}>
点点我,步数不是个事!
</button>
ReactDOM.render(
<StepTracker />,
document.querySelector('#root')
首先,通过调用useState
创建一个新的state
,并将其初始化为0
。它返回steps
的当前值0
和setSteps
函数来更新 steps
,用 increment
函数来对steps
进行增 1 操作。
这里还可以优化的提取increment
函数,可以直接将 increment
函数里面的内联到 onClick
里面:
<button onClick={() => setSteps(steps => steps + 1)}>
I took another step
</button>
示例3: state 作为数组
记住,state
可以保存任何你想要的值。下面是一个随机数列表的例子,单击按钮将向列表添加一个新的随机数:
function RandomList() {
const [items, setItems] = useState([]);
const addItem = () => {
setItems([
...items,
id: items.length,
value: Math.random() * 100
return (
<button onClick={addItem}>Add a number</button>
{items.map(item => (
<li key={item.id}>{item.value}</li>
注意,我们state
初始化为空数组[]
,并在addItem
函数中更新值。
setItems
更新 state
不会将旧值“合并” - 它会使用新值覆盖state
。 这与this.setState
在类中的工作方式不同。
示例4:具有多个键的 state
再来看看,state
为对象的例子,创建一个包含2个字段的登录表单:username
和password
。
下面示例主要展示如何在一个state
对象中存储多个值,以及如何更新单个值。
function LoginForm() {
const [form, setValues] = useState({
username: '',
password: ''
const printValues = e => {
e.preventDefault();
console.log(form.username, form.password);
const updateField = e => {
setValues({
...form,
[e.target.name]: e.target.value
return (
<form onSubmit={printValues}>
<label>
Username:
<input
value={form.username}
name="username"
onChange={updateField}
</label>
<label>
Password:
<input
value={form.password}
name="password"
type="password"
onChange={updateField}
</label>
<button>Submit</button>
</form>
如果想试试,可查看 CodeSandbox。
首先,我们创建一个state
片段,并用一个对象初始化它
const [form, setValues] = useState({
username: '',
password: ''
这看起来像是在类中初始化状态的方式。
还有一个处理提交的函数,其中,e.preventDefault
来阻止页面刷新并打印出表单值。
updateField
函数更有意思。它使用setValues
传递一个对象,为了确保现有的状态不被覆盖,这里使用了展开运算(...form
)。
代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug。
原文:https://daveceddia.com/usestate-hook-examples/
关于Fundebug
Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了20亿+错误事件,付费客户有阳光保险、核桃编程、荔枝FM、掌门1对1、微脉、青团社等众多品牌企业。欢迎大家免费试用!