Skip to content

组件通信大全

组件通信是 React 开发中的重要环节,不同组件之间需要传递数据和事件。本章节将介绍 React 中常见的组件通信方式。

9.1 父传子:props

父组件通过 props 向子组件传递数据,这是最基本的组件通信方式。

基本使用

jsx
// 父组件
function Parent() {
  const message = 'Hello from Parent';
  
  return (
    <div>
      <Child message={message} />
    </div>
  );
}

// 子组件
function Child({ message }) {
  return <p>{message}</p>;
}

传递复杂数据

jsx
// 父组件
function Parent() {
  const user = {
    name: '张三',
    age: 20,
    email: 'zhangsan@example.com'
  };
  
  return (
    <div>
      <Child user={user} />
    </div>
  );
}

// 子组件
function Child({ user }) {
  return (
    <div>
      <h1>{user.name}</h1>
      <p>年龄:{user.age}</p>
      <p>邮箱:{user.email}</p>
    </div>
  );
}

传递函数

jsx
// 父组件
function Parent() {
  const [count, setCount] = useState(0);
  
  function handleIncrement() {
    setCount(count + 1);
  }
  
  return (
    <div>
      <p>计数:{count}</p>
      <Child onIncrement={handleIncrement} />
    </div>
  );
}

// 子组件
function Child({ onIncrement }) {
  return (
    <button onClick={onIncrement}>
      增加计数
    </button>
  );
}

9.2 子传父:回调函数

子组件通过调用父组件传递的回调函数来向父组件传递数据。

基本使用

jsx
// 父组件
function Parent() {
  const [message, setMessage] = useState('');
  
  function handleMessageChange(newMessage) {
    setMessage(newMessage);
  }
  
  return (
    <div>
      <p>子组件消息:{message}</p>
      <Child onMessageChange={handleMessageChange} />
    </div>
  );
}

// 子组件
function Child({ onMessageChange }) {
  const [inputValue, setInputValue] = useState('');
  
  function handleChange(e) {
    setInputValue(e.target.value);
    onMessageChange(e.target.value);
  }
  
  return (
    <input
      type="text"
      value={inputValue}
      onChange={handleChange}
      placeholder="请输入消息"
    />
  );
}

传递多个参数

jsx
// 父组件
function Parent() {
  const [user, setUser] = useState({ name: '', age: 0 });
  
  function handleUserChange(name, age) {
    setUser({ name, age });
  }
  
  return (
    <div>
      <p>姓名:{user.name}</p>
      <p>年龄:{user.age}</p>
      <Child onUserChange={handleUserChange} />
    </div>
  );
}

// 子组件
function Child({ onUserChange }) {
  const [name, setName] = useState('');
  const [age, setAge] = useState('');
  
  function handleSubmit(e) {
    e.preventDefault();
    onUserChange(name, parseInt(age));
  }
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={name}
        onChange={e => setName(e.target.value)}
        placeholder="姓名"
      />
      <input
        type="number"
        value={age}
        onChange={e => setAge(e.target.value)}
        placeholder="年龄"
      />
      <button type="submit">提交</button>
    </form>
  );
}

9.3 跨组件通信:useContext + createContext

对于多层级的组件通信,使用 Context API 可以避免 props 层层传递的问题。

创建 Context

jsx
// src/contexts/ThemeContext.jsx
import { createContext, useState, useContext } from 'react';

// 创建 Context
const ThemeContext = createContext();

// 提供者组件
export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

// 自定义 Hook
export function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within a ThemeProvider');
  }
  return context;
}

使用 Context

jsx
// App.jsx
import { ThemeProvider } from './contexts/ThemeContext';
import Header from './components/Header';
import Main from './components/Main';

function App() {
  return (
    <ThemeProvider>
      <Header />
      <Main />
    </ThemeProvider>
  );
}

// Header.jsx
import { useTheme } from '../contexts/ThemeContext';

function Header() {
  const { theme, setTheme } = useTheme();
  
  return (
    <header style={{ backgroundColor: theme === 'light' ? '#fff' : '#333' }}>
      <h1>Header</h1>
      <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
        切换主题
      </button>
    </header>
  );
}

// Main.jsx
import { useTheme } from '../contexts/ThemeContext';

function Main() {
  const { theme } = useTheme();
  
  return (
    <main style={{ backgroundColor: theme === 'light' ? '#f0f0f0' : '#444', color: theme === 'light' ? '#333' : '#fff' }}>
      <h2>Main Content</h2>
      <p>当前主题:{theme}</p>
    </main>
  );
}

9.4 祖孙组件通信:useContext 或 中间组件转发

使用 useContext

jsx
// 祖父组件
function Grandparent() {
  const [data, setData] = useState('Hello from Grandparent');
  
  return (
    <DataProvider value={{ data, setData }}>
      <Parent />
    </DataProvider>
  );
}

// 父组件(中间组件)
function Parent() {
  return <Child />;
}

// 孙组件
function Child() {
  const { data, setData } = useData();
  
  return (
    <div>
      <p>{data}</p>
      <button onClick={() => setData('Updated by Child')}>
        更新数据
      </button>
    </div>
  );
}

中间组件转发

jsx
// 祖父组件
function Grandparent() {
  const [data, setData] = useState('Hello from Grandparent');
  
  return <Parent data={data} onDataChange={setData} />;
}

// 父组件(中间组件)
function Parent({ data, onDataChange }) {
  return <Child data={data} onDataChange={onDataChange} />;
}

// 孙组件
function Child({ data, onDataChange }) {
  return (
    <div>
      <p>{data}</p>
      <button onClick={() => onDataChange('Updated by Child')}>
        更新数据
      </button>
    </div>
  );
}

9.5 全局状态通信:useReducer + useContext

对于复杂的全局状态管理,可以使用 useReducer 结合 useContext。

创建状态管理

jsx
// src/contexts/CountContext.jsx
import { createContext, useReducer, useContext } from 'react';

// 初始状态
const initialState = {
  count: 0
};

// Reducer 函数
function countReducer(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    case 'RESET':
      return { count: 0 };
    default:
      return state;
  }
}

// 创建 Context
const CountContext = createContext();

// 提供者组件
export function CountProvider({ children }) {
  const [state, dispatch] = useReducer(countReducer, initialState);
  
  return (
    <CountContext.Provider value={{ state, dispatch }}>
      {children}
    </CountContext.Provider>
  );
}

// 自定义 Hook
export function useCount() {
  const context = useContext(CountContext);
  if (!context) {
    throw new Error('useCount must be used within a CountProvider');
  }
  return context;
}

使用全局状态

jsx
// App.jsx
import { CountProvider } from './contexts/CountContext';
import Counter from './components/Counter';
import Display from './components/Display';

function App() {
  return (
    <CountProvider>
      <Counter />
      <Display />
    </CountProvider>
  );
}

// Counter.jsx
import { useCount } from '../contexts/CountContext';

function Counter() {
  const { dispatch } = useCount();
  
  return (
    <div>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>增加</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>减少</button>
      <button onClick={() => dispatch({ type: 'RESET' })}>重置</button>
    </div>
  );
}

// Display.jsx
import { useCount } from '../contexts/CountContext';

function Display() {
  const { state } = useCount();
  
  return <p>计数:{state.count}</p>;
}

9.6 组件通信场景总结

通信方式适用场景优势劣势
Props父子组件通信简单直接不适合多层级通信
回调函数子父组件通信简单直接不适合多层级通信
Context API跨组件通信避免 props 层层传递不适合频繁更新的状态
useReducer + Context复杂全局状态集中管理状态逻辑增加代码复杂度
状态管理库(Redux、Zustand)大型应用强大的状态管理能力学习成本高

新手选型指南

  1. 简单父子通信:使用 Props 和回调函数
  2. 跨组件通信:使用 Context API
  3. 复杂状态管理:使用 useReducer + Context 或状态管理库
  4. 小型应用:优先使用 React 内置的状态管理方案
  5. 大型应用:考虑使用专业的状态管理库

实战练习

练习1:TodoList 组件通信

jsx
// App.jsx
import { useState } from 'react';
import TodoForm from './components/TodoForm';
import TodoList from './components/TodoList';

function App() {
  const [todos, setTodos] = useState([]);
  
  function addTodo(todo) {
    setTodos(prevTodos => [...prevTodos, todo]);
  }
  
  function deleteTodo(index) {
    setTodos(prevTodos => prevTodos.filter((_, i) => i !== index));
  }
  
  return (
    <div>
      <TodoForm onAddTodo={addTodo} />
      <TodoList todos={todos} onDeleteTodo={deleteTodo} />
    </div>
  );
}

// TodoForm.jsx
import { useState } from 'react';

function TodoForm({ onAddTodo }) {
  const [inputValue, setInputValue] = useState('');
  
  function handleSubmit(e) {
    e.preventDefault();
    if (inputValue) {
      onAddTodo(inputValue);
      setInputValue('');
    }
  }
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={inputValue}
        onChange={e => setInputValue(e.target.value)}
        placeholder="请输入任务"
      />
      <button type="submit">添加</button>
    </form>
  );
}

// TodoList.jsx
function TodoList({ todos, onDeleteTodo }) {
  return (
    <ul>
      {todos.map((todo, index) => (
        <li key={index}>
          {todo}
          <button onClick={() => onDeleteTodo(index)}>删除</button>
        </li>
      ))}
    </ul>
  );
}

练习2:Context API 应用

jsx
// src/contexts/UserContext.jsx
import { createContext, useState, useContext } from 'react';

const UserContext = createContext();

export function UserProvider({ children }) {
  const [user, setUser] = useState(null);
  
  function login(username, password) {
    // 模拟登录
    setUser({ username, id: 1 });
  }
  
  function logout() {
    setUser(null);
  }
  
  return (
    <UserContext.Provider value={{ user, login, logout }}>
      {children}
    </UserContext.Provider>
  );
}

export function useUser() {
  const context = useContext(UserContext);
  if (!context) {
    throw new Error('useUser must be used within a UserProvider');
  }
  return context;
}

// App.jsx
import { UserProvider } from './contexts/UserContext';
import LoginForm from './components/LoginForm';
import UserProfile from './components/UserProfile';

function App() {
  return (
    <UserProvider>
      <LoginForm />
      <UserProfile />
    </UserProvider>
  );
}

// LoginForm.jsx
import { useState } from 'react';
import { useUser } from '../contexts/UserContext';

function LoginForm() {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const { user, login } = useUser();
  
  function handleSubmit(e) {
    e.preventDefault();
    login(username, password);
  }
  
  if (user) {
    return <p>已登录:{user.username}</p>;
  }
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={username}
        onChange={e => setUsername(e.target.value)}
        placeholder="用户名"
      />
      <input
        type="password"
        value={password}
        onChange={e => setPassword(e.target.value)}
        placeholder="密码"
      />
      <button type="submit">登录</button>
    </form>
  );
}

// UserProfile.jsx
import { useUser } from '../contexts/UserContext';

function UserProfile() {
  const { user, logout } = useUser();
  
  if (!user) {
    return <p>请先登录</p>;
  }
  
  return (
    <div>
      <h1>用户资料</h1>
      <p>用户名:{user.username}</p>
      <button onClick={logout}>退出登录</button>
    </div>
  );
}

通过本章节的学习,你已经掌握了 React 中常见的组件通信方式。在实际开发中,根据不同的场景选择合适的通信方式,可以使代码更加清晰和易于维护。

© 2026 编程马·菜鸟教程 版权所有