前言:useState是React16.8版本以及后面版本更新最大的改动,也是React最全新的写法,Hook 可以帮助在组件中使用不同的 React 功能,本文来介绍useState的用法以及进阶用法
useState基本用法
为组件添加状态
import { useState } from 'react';
function Component() {
const [age, setAge] = useState(42);
const [name, setName] = useState('唐僧');
return (
<>
<button onClick={handleClick}>修改</button>
</>
);
}
通常使用数组解构来命名状态变量,例如 [age, setAge]。
- 结构内容分别是:该状态变量
当前的 state,设置需要更新的set函数,用改更改state的状态。 set 函数,它允许你在响应交互时将 state 更改为任何其他值
如果需要更新内容,则调用set函数,用法如下
const handleClick = () =>{
setAge('50')
setName('孙悟空')
}
// React 会存储新状态,使用新值重新渲染组件,并更新 UI
更新状态
- 传递更新函数
import { useState } from 'react';
export default function Counter() {
const [age, setAge] = useState(42);
function increment() {
setAge(a => a + 1);
}
return (
<>
<h1>Your age: {age}</h1> //此时的age你会发现
<button onClick={() => {
increment();
increment();
increment();
}}>+3</button>
<button onClick={() => {
increment();
}}>+1</button>
</>
);
}
- 传递下一个状态
注意事项:
- set 函数 仅更新 下一次 渲染的状态变量。如果在调用 set 函数后读取状态变量,则 仍会得到在调用之前显示在屏幕上的旧值。
- 如果你提供的新值与当前 state 相同(由 Object.is 比较确定),React 将 跳过重新渲染该组件及其子组件。这是一种优化。虽然在某些情况下 React 仍然需要在跳过子组件之前调用你的组件,但这不应影响你的代码。
- React 会 批量处理状态更新。它会在所有 事件处理函数运行 并调用其 set 函数后更新屏幕。这可以防止在单个事件期间多次重新渲染。在某些罕见情况下,你需要强制 React 更早地更新屏幕,例如访问 DOM,你可以使用 flushSync。
- 在渲染期间,只允许在当前渲染组件内部调用 set 函数。React 将丢弃其输出并立即尝试使用新状态重新渲染。这种方式很少需要,但你可以使用它来存储 先前渲染中的信息。
基本的 useState Demo
- 计数器
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
const handleClick = ()=> {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
You pressed me {count} times
</button>
);
}
- 文本字段
import { useState } from 'react';
export default function MyInput() {
const [text, setText] = useState('hello');
function handleChange(e) {
setText(e.target.value);
}
return (
<>
<input value={text} onChange={handleChange} />
<p>You typed: {text}</p>
<button onClick={() => setText('hello')}>
Reset
</button>
</>
);
}
- 复选框
import { useState } from 'react';
export default function MyCheckbox() {
const [liked, setLiked] = useState(true);
function handleChange(e) {
setLiked(e.target.checked);
}
return (
<>
<label>
<input
type="checkbox"
checked={liked}
onChange={handleChange}
/>
I liked this
</label>
<p>You {liked ? 'liked' : 'did not like'} this.</p>
</>
);
}
- 表单(两个变量)
import { useState } from 'react';
export default function Form() {
const [name, setName] = useState('Taylor');
const [age, setAge] = useState(42);
return (
<>
<input
value={name}
onChange={e => setName(e.target.value)}
/>
<button onClick={() => setAge(age + 1)}>
Increment age
</button>
<p>Hello, {name}. You are {age}.</p>
</>
);
}
更新状态中的对象和数组
在 React 中,状态被认为是只读的,因此 你应该替换它而不是改变现有对象
// 🚩 不要像下面这样改变一个对象:
form.firstName = 'Taylor';
// ✅ 使用新对象替换 state
setForm({
...form,
firstName: 'Taylor'
});
嵌套对象
import { useState } from "react";
export default function Form() {
const [person, setPerson] = useState({
name: "Niki de Saint Phalle",
artwork: {
title: "Blue Nana",
city: "Hamburg",
image: "https://i.imgur.com/Sd1AgUOm.jpg",
},
});
function handleNameChange(e) {
setPerson({
...person,
name: e.target.value,
});
}
function handleTitleChange(e) {
setPerson({
...person,
artwork: {
...person.artwork,
title: e.target.value,
},
});
}
function handleCityChange(e) {
setPerson({
...person,
artwork: {
...person.artwork,
city: e.target.value,
},
});
}
function handleImageChange(e) {
setPerson({
...person,
artwork: {
...person.artwork,
image: e.target.value,
},
});
}
return (
<>
<label>
Name:
<input value={person.name} onChange={handleNameChange} />
</label>
<label>
Title:
<input value={person.artwork.title} onChange={handleTitleChange} />
</label>
<label>
City:
<input value={person.artwork.city} onChange={handleCityChange} />
</label>
<label>
Image:
<input value={person.artwork.image} onChange={handleImageChange} />
</label>
<p>
<i>{person.artwork.title}</i>
{" by "}
{person.name}
<br />
(located in {person.artwork.city})
</p>
<img src={person.artwork.image} alt={person.artwork.title} />
</>
);
}
数组
// App.jsx
import { useState } from 'react';
import AddTodo from './AddTodo.js';
import TaskList from './TaskList.js';
let nextId = 3;
const initialTodos = [
{ id: 0, title: 'Buy milk', done: true },
{ id: 1, title: 'Eat tacos', done: false },
{ id: 2, title: 'Brew tea', done: false },
];
export default function TaskApp() {
const [todos, setTodos] = useState(initialTodos);
function handleAddTodo(title) {
setTodos([
...todos,
{
id: nextId++,
title: title,
done: false
}
]);
}
function handleChangeTodo(nextTodo) {
setTodos(todos.map(t => {
if (t.id === nextTodo.id) {
return nextTodo;
} else {
return t;
}
}));
}
function handleDeleteTodo(todoId) {
setTodos(
todos.filter(t => t.id !== todoId)
);
}
return (
<>
<AddTodo
onAddTodo={handleAddTodo}
/>
<TaskList
todos={todos}
onChangeTodo={handleChangeTodo}
onDeleteTodo={handleDeleteTodo}
/>
</>
);
}
// AddTodo.jsx
import { useState } from 'react';
export default function AddTodo({ onAddTodo }) {
const [title, setTitle] = useState('');
return (
<>
<input
placeholder="Add todo"
value={title}
onChange={e => setTitle(e.target.value)}
/>
<button onClick={() => {
setTitle('');
onAddTodo(title);
}}>Add</button>
</>
)
}
// TaskList.jsx
import { useState } from 'react';
export default function TaskList({
todos,
onChangeTodo,
onDeleteTodo
}) {
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>
<Task
todo={todo}
onChange={onChangeTodo}
onDelete={onDeleteTodo}
/>
</li>
))}
</ul>
);
}
function Task({ todo, onChange, onDelete }) {
const [isEditing, setIsEditing] = useState(false);
let todoContent;
if (isEditing) {
todoContent = (
<>
<input
value={todo.title}
onChange={e => {
onChange({
...todo,
title: e.target.value
});
}} />
<button onClick={() => setIsEditing(false)}>
Save
</button>
</>
);
} else {
todoContent = (
<>
{todo.title}
<button onClick={() => setIsEditing(true)}>
Edit
</button>
</>
);
}
return (
<label>
<input
type="checkbox"
checked={todo.done}
onChange={e => {
onChange({
...todo,
done: e.target.checked
});
}}
/>
{todoContent}
<button onClick={() => onDelete(todo.id)}>
Delete
</button>
</label>
);
}
用 Immer 编写简洁的更新逻辑
import { useState } from 'react';
import { useImmer } from 'use-immer';
let nextId = 3;
const initialList = [
{ id: 0, title: 'Big Bellies', seen: false },
{ id: 1, title: 'Lunar Landscape', seen: false },
{ id: 2, title: 'Terracotta Army', seen: true },
];
export default function BucketList() {
const [list, updateList] = useImmer(initialList);
function handleToggle(artworkId, nextSeen) {
updateList(draft => {
const artwork = draft.find(a =>
a.id === artworkId
);
artwork.seen = nextSeen;
});
}
return (
<>
<h1>Art Bucket List</h1>
<h2>My list of art to see:</h2>
<ItemList
artworks={list}
onToggle={handleToggle} />
</>
);
}
function ItemList({ artworks, onToggle }) {
return (
<ul>
{artworks.map(artwork => (
<li key={artwork.id}>
<label>
<input
type="checkbox"
checked={artwork.seen}
onChange={e => {
onToggle(
artwork.id,
e.target.checked
);
}}
/>
{artwork.title}
</label>
</li>
))}
</ul>
);
}