三. 数据层面

目录
[3.1 基础数据](#3.1 基础数据)
[3.1.1 State(状态)](#3.1.1 State(状态))
[3.1.2 Props(属性)](#3.1.2 Props(属性))
[3.2 组件通信](#3.2 组件通信)
[3.2.1 父组件向子组件通信------props](#3.2.1 父组件向子组件通信——props)
[3.2.2 子组件向父组件通信------回调函数](#3.2.2 子组件向父组件通信——回调函数)
[3.2.3 兄弟组件通信------以父为中介](#3.2.3 兄弟组件通信——以父为中介)
[3.2.4 跨级组件通信------context API | Redux](#3.2.4 跨级组件通信——context API | Redux)
3.1 基础数据
3.1.1 State(状态)
概念
State是组件内部的数据存储,用于管理组件的动态数据和UI状态。
当state发生变化时,React会自动重新渲染组件,确保UI与数据保持同步。
特性
- 私有性:每个组件的state是独立的,只能在组件内部访问和修改
- 可变性:state可以随着用户交互或数据变化而改变
- 响应式:state变化会触发组件重新渲染
- 异步性:state更新可能是异步的,不能依赖当前state值进行计算
类组件状态管理
javascript
import React, { Component } from 'react';
class Counter extends Component {
// 1. 在constructor中初始化state
constructor(props) {
super(props);
this.state = {
count: 0,
message: "初始消息"
};
}
// 2. 通过setState方法更新state
increment = () => {
// 正确的更新方式
this.setState(prevState => ({
count: prevState.count + 1
}));
// 错误的更新方式(依赖当前state值)
// this.setState({ count: this.state.count + 1 });
};
changeMessage = () => {
this.setState({
message: "消息已更新"
});
};
render() {
return (
<div>
<h1>计数器: {this.state.count}</h1>
<p>{this.state.message}</p>
<button onClick={this.increment}>增加</button>
<button onClick={this.changeMessage}>更新消息</button>
</div>
);
}
}
export default Counter;
函数组件状态管理(使用useState Hook)
javascript
import React, { useState } from 'react';
function Counter() {
// 1. 使用useState Hook初始化state
const [count, setCount] = useState(0);
const [message, setMessage] = useState("初始消息");
// 2. 更新state的函数
const increment = () => {
// 正确的更新方式
setCount(prevCount => prevCount + 1);
// 也可以直接使用
// setCount(count + 1);
};
const changeMessage = () => {
setMessage("消息已更新");
};
return (
<div>
<h1>计数器: {count}</h1>
<p>{message}</p>
<button onClick={increment}>增加</button>
<button onClick={changeMessage}>更新消息</button>
</div>
);
}
export default Counter;
State更新注意事项
- 不要直接修改state:this.state.count = 1; 是错误的
- 使用setState更新:this.setState({ count: 1 });
- 函数式更新:当新state依赖于旧state时,使用函数式更新
- 批量更新:React会批量处理state更新,提高性能
拓展内容------类组件VS函数组件
函数组件
javascript
// 函数组件 - 简单的JavaScript函数
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
类组件
javascript
// 类组件 - 继承自React.Component
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
核心区别对比
|-----------------|------------------|---------------------|
| 特性 | 函数组件 | 类组件 |
| 定义方式 | JavaScript函数 | ES6类继承 |
| 状态管理 | 通过useState Hook | 通过this.state |
| 生命周期 | 通过useEffect Hook | 通过生命周期方法 |
| this 指向 | 无this | 有this |
| 代码复杂度 | 简洁 | 样板代码较多 |
| 性能 | 通常更优 | 开销较大 |
| 逻辑复用 | 通过自定义Hook | 通过高阶组件或render props |
状态管理差异
函数组件(使用Hooks):
javascript
import React, { useState } from 'react';
function Counter() {
// 使用useState Hook管理状态
const [count, setCount] = useState(0);
return (
<div>
<p>计数: {count}</p>
<button onClick={() => setCount(count + 1)}>增加</button>
</div>
);
}
类组件:
javascript
import React, { Component } from 'react';
class Counter extends Component {
// 在constructor中初始化state
constructor(props) {
super(props);
this.state = {
count: 0
};
}
// 通过setState更新状态
increment = () => {
this.setState(prevState => ({
count: prevState.count + 1
}));
};
render() {
return (
<div>
<p>计数: {this.state.count}</p>
<button onClick={this.increment}>增加</button>
</div>
);
}
}
生命周期方法差异
函数组件(使用useEffect):
javascript
import React, { useState, useEffect } from 'react';
function Timer() {
const [time, setTime] = useState(0);
// 模拟componentDidMount和componentDidUpdate
useEffect(() => {
const interval = setInterval(() => {
setTime(prevTime => prevTime + 1);
}, 1000);
// 清理函数(模拟componentWillUnmount)
return () => clearInterval(interval);
}, []); // 空依赖数组表示只在挂载时执行
return <div>时间: {time}秒</div>;
}
类组件:
javascript
import React, { Component } from 'react';
class Timer extends Component {
constructor(props) {
super(props);
this.state = { time: 0 };
}
componentDidMount() {
this.interval = setInterval(() => {
this.setState(prevState => ({
time: prevState.time + 1
}));
}, 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
return <div>时间: {this.state.time}秒</div>;
}
}
为什么现在选择函数组件
(1)函数组件更加简洁,减少了样板代码:
函数组件 - 5行代码
javascript
function UserProfile({ user }) {
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
类组件 - 15行代码
javascript
class UserProfile extends React.Component {
render() {
const { user } = this.props;
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
}
(2)函数组件通过自定义Hook实现更好的逻辑复用:
自定义Hook - 可复用的状态逻辑
javascript
function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
const increment = () => setCount(c => c + 1);
const decrement = () => setCount(c => c - 1);
const reset = () => setCount(initialValue);
return { count, increment, decrement, reset };
}
在多个组件中使用
javascript
function Counter1() {
const { count, increment, decrement } = useCounter();
return (
<div>
<p>计数: {count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
);
}
function Counter2() {
const { count, increment, decrement, reset } = useCounter(100);
return (
<div>
<p>计数: {count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
<button onClick={reset}>重置</button>
</div>
);
}
(3)函数组件具有更好的性能表现:
- 更小的内存占用:函数组件不需要实例化,直接执行函数
- 更快的渲染:React可以更有效地优化函数组件的渲染
- 避免不必要的重新渲染:通过useMemo和useCallback可以更好地控制渲染
(4)函数组件的代码更易于阅读和理解:
函数组件 - 逻辑清晰,易于理解
javascript
function UserProfile({ user, onUpdate }) {
const [isEditing, setIsEditing] = useState(false);
const [name, setName] = useState(user.name);
useEffect(() => {
if (!isEditing) {
setName(user.name);
}
}, [user.name, isEditing]);
const handleSave = () => {
onUpdate({ ...user, name });
setIsEditing(false);
};
return (
<div>
{isEditing ? (
<input
value={name}
onChange={e => setName(e.target.value)}
onBlur={handleSave}
/>
) : (
<h2 onClick={() => setIsEditing(true)}>{name}</h2>
)}
</div>
);
}
(5)函数组件是React的未来发展方向:
- 持续更新:React团队持续为函数组件添加新特性
- Hooks生态:丰富的Hooks生态系统支持各种复杂场景
- TypeScript友好:函数组件与TypeScript结合更加自然
优先使用函数组件的情况
- 展示型组件(无状态或简单状态)
- 需要逻辑复用的组件
- 性能敏感的组件
- 新项目开发
考虑使用类组件的情况
- 需要兼容旧版React
- 特定场景需要类特性
- 复杂的状态管理和生命周期逻辑
- 维护现有类组件代码
3.1.2 Props(属性)
概念
Props(Properties的缩写)是父组件向子组件传递数据的方式。
它们是只读的,子组件不能修改props,只能接收和使用。
特性
- 单向数据流:数据从父组件流向子组件
- 只读性:子组件不能修改props
- 灵活性:可以传递任何JavaScript值(字符串、数字、对象、函数等)
- 默认值:可以通过defaultProps设置默认值
Props使用示例
父组件
javascript
import React from 'react';
function ParentComponent() {
const user = {
name: "张三",
age: 25,
hobbies: ["阅读", "编程", "旅行"]
};
return (
<div>
<h1>父组件</h1>
{/* 传递字符串、数字、对象等不同类型的props */}
<ChildComponent
name="李四"
age={30}
isActive={true}
user={user}
onButtonClick={() => alert("按钮被点击了!")}
/>
</div>
);
}
子组件
javascript
function ChildComponent(props) {
return (
<div>
<h2>子组件</h2>
<p>姓名: {props.name}</p>
<p>年龄: {props.age}</p>
<p>是否活跃: {props.isActive ? "是" : "否"}</p>
{/* 访问对象props */}
<p>用户名: {props.user.name}</p>
<p>年龄: {props.user.age}</p>
{/* 调用函数props */}
<button onClick={props.onButtonClick}>点击我</button>
</div>
);
}
export default ParentComponent;
Props注意事项
只读性:子组件不能修改props
javascript
// 错误的修改方式
props.name = "王五"; // 会抛出错误
默认值设置:
javascript
function ChildComponent({ name = "默认名称", age = 18 }) {
return <div>{name} - {age}</div>;
}
// 或者使用defaultProps
ChildComponent.defaultProps = {
name: "默认名称",
age: 18
};
类型检查(使用PropTypes):
javascript
import PropTypes from 'prop-types';
function ChildComponent({ name, age, isActive }) {
return (
<div>
<p>姓名: {name}</p>
<p>年龄: {age}</p>
<p>是否活跃: {isActive ? "是" : "否"}</p>
</div>
);
}
ChildComponent.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
isActive: PropTypes.bool
};
3.2 组件通信
3.2.1 父组件向子组件通信------props

概念
父组件通过props将数据传递给子组件,子组件接收这些props并在内部使用。
示例
父组件
javascript
import React from 'react';
function ParentComponent() {
const [message, setMessage] = useState("欢迎来到React世界!");
const [count, setCount] = useState(0);
return (
<div>
<h1>父组件</h1>
<p>当前计数: {count}</p>
{/* 传递数据给子组件 */}
<ChildComponent
message={message}
count={count}
onIncrement={() => setCount(count + 1)}
/>
</div>
);
}
子组件
javascript
function ChildComponent({ message, count, onIncrement }) {
return (
<div>
<h2>子组件</h2>
<p>{message}</p>
<p>接收的计数: {count}</p>
<button onClick={onIncrement}>增加计数</button>
</div>
);
}
export default ParentComponent;
特点
- 简单直接,是React中最常用的通信方式
- 适用于父子组件层级较近的情况
- 数据流向清晰,易于理解和维护
3.2.2 子组件向父组件通信------回调函数
概念
子组件通过调用父组件传递的回调函数,将数据或事件通知父组件,父组件接收到信息后更新自己的state。
- 父:
- 定义回调函数
- Return()中写入子组件标签
- 给标签绑定属性并以回调函数为props参数传入
- 子:
- 接收props
- 定义变量
- 在组件中使用属性传入定义的变量值作参数
示例
父组件
javascript
import React, { useState } from 'react';
function ParentComponent() {
const [childMessage, setChildMessage] = useState("");
const [childCount, setChildCount] = useState(0);
// 接收子组件数据的回调函数
const handleChildMessage = (msg) => {
setChildMessage(msg);
};
const handleChildCount = (count) => {
setChildCount(count);
};
return (
<div>
<h1>父组件</h1>
<p>子组件发送的消息: {childMessage}</p>
<p>子组件的计数: {childCount}</p>
{/* 传递回调函数给子组件 */}
<ChildComponent
onSendMessage={handleChildMessage}
onSendCount={handleChildCount}
/>
</div>
);
}
子组件
javascript
function ChildComponent({ onSendMessage, onSendCount }) {
const [internalMessage, setInternalMessage] = useState("这是子组件的消息");
const [internalCount, setInternalCount] = useState(5);
const sendMessage = () => {
// 调用父组件的回调函数
onSendMessage(internalMessage);
};
const sendCount = () => {
onSendCount(internalCount);
};
return (
<div>
<h2>子组件</h2>
<p>内部消息: {internalMessage}</p>
<p>内部计数: {internalCount}</p>
<button onClick={sendMessage}>发送消息给父组件</button>
<button onClick={sendCount}>发送计数给父组件</button>
</div>
);
}
export default ParentComponent;
特点
- 通过回调函数实现双向通信
- 父组件控制状态,子组件触发状态更新
- 适用于需要子组件通知父组件的场景
3.2.3 兄弟组件通信------以父为中介

概念
当两个组件不是父子关系但有相同的父组件时,称为兄弟组件。
兄弟组件不能直接相互传送数据,需要通过状态提升的方式,将共享状态保存到最近的共同父组件中。
示例
父组件(状态提升)
javascript
import React, { useState } from 'react';
function ParentComponent() {
// 共享状态提升到父组件
const [sharedMessage, setSharedMessage] = useState("共享消息");
const [sharedCount, setSharedCount] = useState(0);
// 提供修改共享状态的函数
const updateSharedMessage = (msg) => {
setSharedMessage(msg);
};
const updateSharedCount = (count) => {
setSharedCount(count);
};
return (
<div>
<h1>父组件(状态提升)</h1>
<p>共享消息: {sharedMessage}</p>
<p>共享计数: {sharedCount}</p>
{/* 两个兄弟组件 */}
<SiblingComponent1
sharedMessage={sharedMessage}
onUpdateMessage={updateSharedMessage}
/>
<SiblingComponent2
sharedCount={sharedCount}
onUpdateCount={updateSharedCount}
/>
</div>
);
}
兄弟组件1(send)
javascript
function SiblingComponent1({ sharedMessage, onUpdateMessage }) {
const [localMessage, setLocalMessage] = useState("组件1的消息");
const sendMessage = () => {
onUpdateMessage(localMessage);
};
return (
<div style={{ border: '1px solid blue', padding: '10px', margin: '10px' }}>
<h2>兄弟组件1</h2>
<p>当前共享消息: {sharedMessage}</p>
<p>本地消息: {localMessage}</p>
<button onClick={sendMessage}>发送消息到父组件</button>
<button onClick={() => setLocalMessage("组件1的新消息")}>更新本地消息</button>
</div>
);
}
兄弟组件2(receive)
javascript
function SiblingComponent2({ sharedCount, onUpdateCount }) {
const [localCount, setLocalCount] = useState(10);
const sendCount = () => {
onUpdateCount(localCount);
};
return (
<div style={{ border: '1px solid green', padding: '10px', margin: '10px' }}>
<h2>兄弟组件2</h2>
<p>当前共享计数: {sharedCount}</p>
<p>本地计数: {localCount}</p>
<button onClick={sendCount}>发送计数到父组件</button>
<button onClick={() => setLocalCount(localCount + 1)}>增加本地计数</button>
</div>
);
}
export default ParentComponent;
特点
- 通过父组件作为中间人实现兄弟组件通信
- 父组件管理共享状态,兄弟组件通过回调函数更新状态
- 避免了兄弟组件之间的直接依赖
3.2.4 跨级组件通信------context API | Redux

概念
当组件层级较深时,通过props层层传递数据会导致代码复杂且难以维护,这时可以使用Context API或状态管理库(如Redux)来实现跨级组件通信。
方法1:使用Context API
javascript
// 1. 创建Context
import React, { createContext, useContext, useState } from 'react';
// 创建Context
const MessageContext = createContext();
// 2. 父组件提供Context
function GrandparentComponent() {
const [message, setMessage] = useState("跨级通信消息");
const [count, setCount] = useState(100);
return (
<MessageContext.Provider value={{ message, count, setMessage, setCount }}>
<h1>祖父组件</h1>
<ParentComponent />
</MessageContext.Provider>
);
}
// 3. 中间组件(不需要传递props)
function ParentComponent() {
return (
<div>
<h2>父组件</h2>
<ChildComponent />
</div>
);
}
// 4. 子组件消费Context
function ChildComponent() {
// 使用useContext获取Context值
const { message, count, setMessage, setCount } = useContext(MessageContext);
return (
<div style={{ border: '1px solid red', padding: '10px', margin: '10px' }}>
<h3>子组件</h3>
<p>从Context获取的消息: {message}</p>
<p>从Context获取的计数: {count}</p>
<button onClick={() => setMessage("消息已更新")}>更新消息</button>
<button onClick={() => setCount(count + 1)}>增加计数</button>
</div>
);
}
export default GrandparentComponent;
方法2:使用Redux(状态管理库)
javascript
// store.js - 创建Redux store
import { createStore } from 'redux';
// Reducer
function counterReducer(state = { count: 0, message: "Redux消息" }, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
case 'UPDATE_MESSAGE':
return { ...state, message: action.payload };
default:
return state;
}
}
// 创建store
const store = createStore(counterReducer);
export default store;
// App.js - 提供store
import React from 'react';
import { Provider } from 'react-redux';
import store from './store';
import GrandchildComponent from './GrandchildComponent';
function App() {
return (
<Provider store={store}>
<div>
<h1>根组件</h1>
<GrandchildComponent />
</div>
</Provider>
);
}
export default App;
// GrandchildComponent.js - 任意层级的组件
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
function GrandchildComponent() {
// 使用useSelector获取state
const { count, message } = useSelector(state => state);
// 使用useDispatch获取dispatch函数
const dispatch = useDispatch();
return (
<div style={{ border: '1px solid purple', padding: '10px', margin: '10px' }}>
<h3>任意层级的组件</h3>
<p>计数: {count}</p>
<p>消息: {message}</p>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>增加计数</button>
<button onClick={() => dispatch({ type: 'UPDATE_MESSAGE', payload: '新消息' })}>更新消息</button>
</div>
);
}
特点
- Context API适用于中等规模的应用,简单易用
- Redux适用于大型复杂应用,提供更强大的状态管理
- 跨级通信避免了props drilling(props层层传递)
- 保持组件的独立性和可复用性