State是组件的内存
网页交互过程中,组件通常需要根据交互来更改屏幕上显示的内容。输入表单应该更新输入字段,单击图像轮播上的“下一步”应该更改显示的图像,单击“购买”应该将产品放入购物车。组件需要记住一些特定的值,当前所发生的交互变化,用于储存这种变化的叫做State
export default function App() {
let index = 0;
function handleClick() {
index = index + 1;
}
return (
<>
<button onClick={handleClick}>
Next
</button>
<p>{index}</p>
</>
);
}
这是一段JSX代码,按理来说在渲染过后会出现一个NextButton和一个数字,点击Button以后数字值会增加1,但是实际点击过后页面上的数字0并不会跳转到1.
handleClick()更新局部变量index
. 但是有两件事导致页面没有发生变化:
- 局部变量在渲染之间不会持续存在。当 React 第二次渲染这个组件时,它会从头开始渲染它——它不考虑对局部变量的任何更改。
- 对局部变量的更改不会触发渲染。React 没有意识到它需要使用新数据再次渲染组件。
要使用新数据更新组件,需要做两件事:
- 保留渲染之间的数据。
- 触发React 以使用新数据渲染组件(重新渲染)。
useStateHook 提供了这两件事:
- 用于在渲染之间保留数据的状态变量。
- 一个状态设置函数,用于更新变量并触发 React 再次渲染组件。
useState()
useState是一个 React Hook,它让组件“记住”一些信息(State)。它返回两个值:当前状态和可用于更新它的函数。
const [state, setState] = useState(initialState);
您可以通过三个步骤向组件添加状态:
State是组件的内存。状态变量允许您保存一些随时间变化的信息,并且只限定于于你的组件。上图中,count保存点击次数。你可以将任何 JavaScript 值保持在State - 例如,当前输入文本、选定的图库图像或购物车的内容。
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
You pressed me {count} times
</button>
);
}
对于这一段代码,效果于第一段没有使用State的代码大致相同,但是,在这个组件所渲染的页面中,单击Button时count值会实时变化渲染,这是因为使用了state,React知道它需要重新将新的值渲染到当前的页面上
useState的作用
当你调用 时useState,你是在告诉 React 你希望这个组件记住一些东西:
const [index, setIndex] = useState(0);
在这种情况下,您希望 React 记住index.
惯例是将这对命名为const [thing, setThing]. 您可以将其命名为您喜欢的任何名称,但约定使跨项目的事情更容易理解。
useState唯一的参数是你的状态变量的初始值。在此示例中,index的初始值设置0为
useState(0)。
每次渲染组件时,useState都会为您提供一个包含两个值的数组:
- 状态变量(index) 与您存储的值。
- 状态设置函数setIndex()可以更新状态变量并触发 React 再次渲染组件。
当这段代码打入后会发生的情况:
const [index, setIndex] = useState(0);
- 第一次渲染时。你对useState传参0,因此会将index的初始值设置为0,所以它会返回 [0, setIndex]。React 记住0是最新的状态值。
- 状态有所更新时。当用户单击按钮时,它会调用setIndex(index + 1).当前index是0,所以是setIndex(1)。此时React就知道,index值被更新为了1,并且重新渲染用于呈现新画面。
- 组件第二次渲染。React 仍然会读取到useState(0),但是因为 React也会记得你设置了index值为1,所以它会返回[1, setIndex]。
如果你有两个相同的组件调用,他们的State的值是独立的,一个组件的State值改变不会影响到另一个组件
声明State
您可以在一个组件中声明多个State变量。您必须在组件的顶层声明它们,在任何条件或循环语句之外。该组件声明状态变量称为name和age:
这使得组件可以“记住”多个独立的事物State——例如,不同的表单字段。
import { useState } from 'react';
export default function Form() {
const [name, setName] = useState('Taylor');
const [age, setAge] = useState(28);
return (
<>
<input
value={name}
onChange={e => setName(e.target.value)}
/>
<button onClick={() => setAge(age + 1)}>
Happy birthday!
</button>
<p>Hello, {name}. You are {age}.</p>
</>
);
}
在这个例子中,我们有两个State,分别是name和age,我们在表单中输入相你想输入的名字,onChange触发name的set函数setName将输入的值设定为name的值,然后value属性获取name赋给表单更新输入框内容,同时更新下方渲染的名字。按下button使得age值加1,使得下方的年龄数也得到更新。
什么时候不需要使用
- 当常规变量起作用时,不要使用State。状态仅用于在重新渲染之间保持信息。
- 不要添加冗余状态。如果您可以在渲染期间计算某些内容,则不需要它的状态。
更新状态中的对象和数组
你也可以保持对象和数组的状态。但是,您应该始终替换状态中的对象,而不是修改现有对象。更新对象和更新数组描述了有助于避免错误的常见模式。
import { useState } from 'react';
export default function MovingDot() {
const [position, setPosition] = useState({
x: 0,
y: 0
});
return (
<div
onPointerMove={e => {
setPosition({
x: e.clientX,
y: e.clientY
});
}}
style={{
position: 'relative',
width: '100vw',
height: '100vh',
}}>
<div style={{
position: 'absolute',
backgroundColor: 'red',
borderRadius: '50%',
transform: `translate(${position.x}px, ${position.y}px)`,
left: -10,
top: -10,
width: 20,
height: 20,
}} />
</div>
)
}
特别情况
将相同的值传递给 setState
如果将当前状态传递给setState,React将跳过重新渲染组件:
setCount(count); // Won't trigger a re-render
这是性能优化。React 使用Object.is()算法来比较这些值。
将更新程序函数传递给setState
您可以将函数传递给setState. 这样的函数,就像在这个例子中一样,被称为“更新器”。React 将在下一次渲染期间调用您的更新程序来计算最终状态。
此文章来源于React新文档React 中文文档(Beta 版) | React 中文文档 | React 中文网