React交互、状态管理与状态共享详解
立即解锁
发布时间: 2025-09-06 01:56:10 阅读量: 3 订阅数: 53 AIGC 

# React交互、状态管理与状态共享详解
## 1. React与页面集成
要让React控制页面上的某个DOM元素,需要完成以下操作:
1. **获取特定ID的DOM元素**:在`app/views/concerts/show.html.erb`文件中,将原本渲染座位的部分替换为`<div id="react-element" />`。
2. **加载打包文件**:在`app/layouts/application.html.erb`布局文件中添加如下代码:
```erb
<%= javascript_pack_tag(
"application",
"venue_display",
"data-turbo-track": "reload"
) %>
```
这样,`javascript_pack_tag`就包含了两个打包文件。不过需要注意的是,在编写本文时,两次单独调用`javascript_pack_tag`会因Turbo被加载两次而触发错误,希望后续能得到修复。
从性能角度来看,将所有打包文件都放在头部并非理想做法,但Turbo Drive意味着Rails在每个会话中实际上不会多次重新加载头部,并且webpack会进行一些优化。
## 2. 交互性、状态与钩子
### 2.1 状态管理的必要性
React接管页面部分区域并绘制座位后,我们希望它能对某些操作做出响应,实现交互性。在React中,传递给组件的props是不可变的,若要改变组件的某些属性,就不能使用props。React引入了“状态(state)”的概念,用于表示组件中会发生变化并触发组件显示更新的部分。
### 2.2 钩子(Hooks)的使用
从React 16.8版本开始引入了钩子(Hooks),在此之前,函数式组件无法管理变化的状态,而类组件可以通过不同机制管理状态。React核心团队表示,钩子和函数式组件是未来的发展方向,因此本文将重点介绍如何使用钩子来管理状态。
以下是一个点击时改变状态的`Seat`组件代码:
```typescript
import * as React from "react"
interface SeatProps {
seatNumber: number
initialStatus: string
}
const Seat = ({
seatNumber,
initialStatus,
}: SeatProps): React.ReactElement => {
const [status, setStatus] = React.useState(initialStatus)
function changeState(): void {
if (status === "held") {
setStatus("unsold")
} else {
setStatus("held")
}
}
function stateDisplayClass(): string {
if (status === "held") {
return "bg-green-500"
} else {
return "bg-white hover:bg-blue-300"
}
}
const cssClass = "p-4 m-2 border-black border-4 text-lg"
return (
<td>
<span
className={`${cssClass} ${stateDisplayClass()}`}
onClick={changeState}>
{seatNumber + 1}
</span>
</td>
)
}
export default Seat
```
在上述代码中,关键的一行是`const [status, setStatus] = React.useState(initialStatus)`,这里调用了`React.useState`方法,它是一个React钩子方法。`useState`的作用是将给定的值注册为React状态的一部分,当该值发生变化时会触发重新渲染。其参数是新状态对象的初始值,这里从作为props传入的`initialStatus`获取。
`useState`方法返回一个包含两个元素的数组,通常使用JavaScript的解构语法将其捕获到两个不同的变量中。第一个返回值`status`是当前状态的值,第二个返回值`setStatus`是状态设置函数,用于改变状态值并触发重新绘制。
需要注意的是,传递给`useState`的参数仅在组件首次渲染时使用,后续重新渲染时,组件会跟踪现有状态,不再需要或使用初始值。
### 2.3 事件处理与状态更新
在组件的JSX返回值中,有两个重要的变化:
- `className`现在包含对`stateDisplayClass()`函数的调用。
- 为`span`元素添加了`onClick={changeState}`属性。
`onClick`属性是React进行事件处理的方式,当按钮被点击时,`onClick`事件触发,调用`changeState`函数。在`changeState`函数中,根据当前`status`的值,使用`setStatus`函数更新状态,从而触发元素的重新绘制。
### 2.4 使用钩子的注意事项
- **使用范围**:钩子只能在函数式组件中使用,并且只能在函数的顶级声明,不能在嵌套函数、循环或`if`语句中使用。
- **多值状态管理**:如果要管理多个状态值,可以多次调用`useState`来获取每个值的设置器,也可以将初始值设置为数组或对象。如果状态值更复杂,可能有其他更易用的钩子。
- **类型问题**:如果`status`使用字符串作为值,但只有有限的有效字符串值,TypeScript提供了相应的机制来处理。
## 3. 状态共享
### 3.1 状态共享的场景与模式
当状态完全封装在一个组件内部时,钩子和`useState`可以很好地工作,但组件之间通常需要共享状态。在React中,当所有需要某个状态的组件有一个共同的父组件时,该状态通常由共同的父组件拥有。父组件将原始值作为props传递给子组件,并通常还会传递一个函数,子组件可以调用该函数来改变父组件的状态。当子组件调用该函数时,父组件的状态改变,然后父组件重新渲染,导致子组件使用新的props值重新渲染。这种模式通常被称为“提升状态”和“传递状态”。
### 3.2 示例:座位购买系统
假设我们可以使用下拉菜单来确定一次要购买的座位数量,点击座位时,会选择该座位右侧的相应数量的座位进行购买。如果右侧没有足够的座位,则该座位标记为不可用,点击无效。
这个功能引入了一个新的状态:通过下拉菜单选择的要购买的座位数量。同时,`Seat`组件需要了解其所在行的其他座位的信息来确定自身状态。
以下是相关组件的代码实现:
#### 3.2.1 `Venue`组件
```typescript
import * as React from "react"
import VenueBody from "components/venue_body"
import VenueHeader from "components/venue_header"
interface VenueProps {
rows: number
seatsPerRow: number
}
const Venue = ({ rows, seatsPerRow }: VenueProps): React.ReactElement => {
const [ticketsToBuyCount, setTicketsToBuyCount] = React.useState(1)
return (
<>
<VenueHeader
seatsPerRow={seatsPerRow}
setTicketsToBuyCount={setTicketsToBuyCount}
/>
<
```
0
0
复制全文
相关推荐










