参考文章
组件通常需要根据交互更改屏幕上显示的内容。输入表单应该更新输入字段,单击轮播图上的“下一个”应该更改显示的图片,单击“购买”应该将商品放入购物车。组件需要“记住”某些东西:当前输入值、当前图片、购物车。在 React 中,这种组件特有的记忆被称为 state。
以下是一个渲染雕塑图片的组件。点击 “Next” 按钮应该显示下一个雕塑并将 index
更改为 1
,再次点击又更改为 2
,以此类推。但这个组件现在不起作用:
import { sculptureList } from './data.js';
export default function Gallery() {
let index = 0;
function handleClick() {
index = index + 1;
}
let sculpture = sculptureList[index];
return (
<>
<button onClick={handleClick}>
Next
</button>
<h2>
<i>{sculpture.name} </i>
by {sculpture.artist}
</h2>
<h3>
({index + 1} of {sculptureList.length})
</h3>
<img src={sculpture.url} alt={sculpture.alt}/>
<p>
{sculpture.description}
</p>
</>
);
}
handleClick()
事件处理函数正在更新局部变量 index
。但存在两个原因使得变化不可见:
要使用新数据更新组件,需要做两件事:
useState
Hook 提供了这两个功能:
要添加 state 变量,先从文件顶部的 React 中导入 useState
:
import { useState } from 'react';
然后,替换这一行:
let index = 0;
将其修改为
const [index, setIndex] = useState(0);
index
是一个 state 变量,setIndex
是对应的 setter 函数。
这里的
[
和]
语法称为数组解构,它允许从数组中读取值。useState
返回的数组总是正好有两项。
以下展示了它们在 handleClick()
中是如何共同起作用的:
function handleClick() {
setIndex(index + 1);
}
现在点击 “Next” 按钮切换当前雕塑:
import { useState } from 'react';
import { sculptureList } from './data.js';
export default function Gallery() {
const [index, setIndex] = useState(0);
function handleClick() {
setIndex(index + 1);
}
let sculpture = sculptureList[index];
return (
<>
<button onClick={handleClick}>
Next
</button>
<h2>
<i>{sculpture.name} </i>
by {sculpture.artist}
</h2>
<h3>
({index + 1} of {sculptureList.length})
</h3>
<img src={sculpture.url} alt={sculpture.alt}/>
<p>
{sculpture.description}
</p>
</>
);
}
在 React 中,useState
以及任何其他以“use
”开头的函数都被称为 Hook。
Hook 是特殊的函数,只在 React 渲染时有效。它们能让你 “hook” 到不同的 React 特性中去。
State 只是这些特性中的一个,之后还会遇到其他 Hook。
注意:Hooks ——以 use
开头的函数——只能在组件或自定义 Hook 的最顶层调用。 不能在条件语句、循环语句或其他嵌套函数内调用 Hook。Hook 是函数,但将它们视为关于组件需求的无条件声明会很有帮助。在组件顶部 “use” React 特性,类似于在文件顶部“导入”模块。
useState
当调用 useState
时,是在告诉 React 想让这个组件记住一些东西:
const [index, setIndex] = useState(0);
在这个例子里,希望 React 记住 index
。
注意:惯例是将这对返回值命名为 const [thing, setThing]
。也可以将其命名为任何喜欢的名称,但遵照约定俗成能使跨项目合作更易理解。
useState
的唯一参数是 state 变量的初始值。在这个例子中,index
的初始值被useState(0)
设置为 0
。
每次组件渲染时,useState
都会给一个包含两个值的数组:
index
) 会保存上次渲染的值。setIndex
) 可以更新 state 变量并触发 React 重新渲染组件。以下是实际发生的情况:
const [index, setIndex] = useState(0);
0
作为 index
的初始值传递给 useState
,它将返回 [0, setIndex]
。 React 记住 0
是最新的 state 值。setIndex(index + 1)
。 index
是 0
,所以它是 setIndex(1)
。这告诉 React 现在记住 index
是 1
并触发下一次渲染。useState(0)
,但是因为 React 记住 了将 index
设置为了 1
,它将返回 [1, setIndex]
。可以在一个组件中拥有任意多种类型的 state 变量。该组件有两个 state 变量,一个数字 index
和一个布尔值 showMore
,点击 “Show Details” 会改变 showMore
的值:
import { useState } from 'react';
import { sculptureList } from './data.js';
export default function Gallery() {
const [index, setIndex] = useState(0);
const [showMore, setShowMore] = useState(false);
function handleNextClick() {
setIndex(index + 1);
}
function handleMoreClick() {
setShowMore(!showMore);
}
let sculpture = sculptureList[index];
return (
<>
<button onClick={handleNextClick}>
Next
</button>
<h2>
<i>{sculpture.name} </i>
by {sculpture.artist}
</h2>
<h3>
({index + 1} of {sculptureList.length})
</h3>
<button onClick={handleMoreClick}>
{showMore ? 'Hide' : 'Show'} details
</button>
{showMore && <p>{sculpture.description}</p>}
<img src={sculpture.url} alt={sculpture.alt}/>
</>
);
}
如果它们不相关,那么存在多个 state 变量是一个好主意,例如本例中的 index
和 showMore
。但是,如果发现经常同时更改两个 state 变量,那么最好将它们合并为一个。例如,如果有一个包含多个字段的表单,那么有一个值为对象的 state 变量比每个字段对应一个 state 变量更方便。 选择 state 结构在这方面有更多提示。
State 是屏幕上组件实例内部的状态。换句话说,如果渲染同一个组件两次,每个副本都会有完全隔离的 state!改变其中一个不会影响另一个。
在这个例子中,之前的 Gallery
组件以同样的逻辑被渲染了两次。试着点击每个画廊内的按钮。会注意到它们的 state 是相互独立的:
import Gallery from './Gallery.js';
export default function Page() {
return (
<div className="Page">
<Gallery />
<Gallery />
</div>
);
}
这就是 state 与声明在模块顶部的普通变量不同的原因。 State 不依赖于特定的函数调用或在代码中的位置,它的作用域“只限于”屏幕上的某块特定区域。渲染了两个
组件,所以它们的 state 是分别存储的。
还要注意 Page
组件“不知道”关于 Gallery
state 的任何信息,甚至不知道它是否有任何 state。与 props 不同,state 完全私有于声明它的组件。父组件无法更改它。这使你可以向任何组件添加或删除 state,而不会影响其他组件。
如果希望两个画廊保持其 states 同步怎么办?在 React 中执行此操作的正确方法是从子组件中删除 state 并将其添加到离它们最近的共享父组件中。也叫做状态提升。
useState
Hook 来声明的。use
开头的特殊函数。它们能让你 “hook” 到像 state 这样的 React 特性中。useState
,仅在组件或另一个 Hook 的顶层被调用才有效。useState
Hook 返回一对值:当前 state 和更新它的函数。