Skip to content

React组件通信

React中组件间的通信是构建复杂应用的核心概念。根据组件间的关系,通信方式主要分为以下几类:

1. 父子组件通信

1.1 父传子(Props)

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

jsx
// 父组件
function Parent() {
  const [message, setMessage] = useState('Hello from parent');
  
  return <Child message={message} />;
}

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

1.2 子传父(回调函数)

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

jsx
// 父组件
function Parent() {
  const [childData, setChildData] = useState('');
  
  const handleChildData = (data) => {
    setChildData(data);
  };
  
  return (
    <div>
      <p>来自子组件的数据:{childData}</p>
      <Child onDataChange={handleChildData} />
    </div>
  );
}

// 子组件
function Child({ onDataChange }) {
  const sendDataToParent = () => {
    onDataChange('Hello from child');
  };
  
  return <button onClick={sendDataToParent}>发送数据给父组件</button>;
}

2. 兄弟组件通信

2.1 通过共同的父组件

兄弟组件通过共同的父组件进行状态提升来实现通信。

jsx
function Parent() {
  const [sharedData, setSharedData] = useState('');
  
  return (
    <div>
      <Brother1 onDataChange={setSharedData} />
      <Brother2 data={sharedData} />
    </div>
  );
}

function Brother1({ onDataChange }) {
  return (
    <button onClick={() => onDataChange('来自兄弟1的数据')}>
      发送数据
    </button>
  );
}

function Brother2({ data }) {
  return <div>接收到的数据:{data}</div>;
}

3. 跨层级组件通信

3.1 Context API

适用于跨多层级的组件通信,避免props drilling问题。

jsx
// 创建Context
const UserContext = createContext();

// Provider组件
function App() {
  const [user, setUser] = useState({ name: 'John', age: 30 });
  
  return (
    <UserContext.Provider value={{ user, setUser }}>
      <Parent />
    </UserContext.Provider>
  );
}

// 深层子组件使用Context
function DeepChild() {
  const { user, setUser } = useContext(UserContext);
  
  return (
    <div>
      <p>用户:{user.name}</p>
      <button onClick={() => setUser({...user, name: 'Jane'})}>
        修改用户名
      </button>
    </div>
  );
}

3.2 自定义Hook + Context

结合自定义Hook使用Context,提供更好的封装。

jsx
// 自定义Hook
function useUser() {
  const context = useContext(UserContext);
  if (!context) {
    throw new Error('useUser must be used within UserProvider');
  }
  return context;
}

// 使用
function Component() {
  const { user, setUser } = useUser();
  // ...
}

4. 全局状态管理

4.1 Redux

适用于大型应用的状态管理。

jsx
// Redux Store
import { createStore } from 'redux';

const initialState = {
  count: 0
};

function counterReducer(state = initialState, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, count: state.count + 1 };
    case 'DECREMENT':
      return { ...state, count: state.count - 1 };
    default:
      return state;
  }
}

const store = createStore(counterReducer);

// React组件
import { useSelector, useDispatch } from 'react-redux';

function Counter() {
  const count = useSelector(state => state.count);
  const dispatch = useDispatch();
  
  return (
    <div>
      <p>计数:{count}</p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>
        增加
      </button>
    </div>
  );
}

4.2 Zustand

轻量级的状态管理库。

jsx
import { create } from 'zustand';

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
}));

function Counter() {
  const { count, increment, decrement } = useStore();
  
  return (
    <div>
      <p>计数:{count}</p>
      <button onClick={increment}>增加</button>
      <button onClick={decrement}>减少</button>
    </div>
  );
}

5. 事件总线(EventBus)

适用于复杂的跨组件通信场景。

jsx
// 简单的事件总线实现
class EventBus {
  constructor() {
    this.events = {};
  }
  
  on(event, callback) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(callback);
  }
  
  emit(event, data) {
    if (this.events[event]) {
      this.events[event].forEach(callback => callback(data));
    }
  }
  
  off(event, callback) {
    if (this.events[event]) {
      this.events[event] = this.events[event].filter(cb => cb !== callback);
    }
  }
}

const eventBus = new EventBus();

// 组件A
function ComponentA() {
  const sendMessage = () => {
    eventBus.emit('message', 'Hello from A');
  };
  
  return <button onClick={sendMessage}>发送消息</button>;
}

// 组件B
function ComponentB() {
  const [message, setMessage] = useState('');
  
  useEffect(() => {
    const handleMessage = (data) => {
      setMessage(data);
    };
    
    eventBus.on('message', handleMessage);
    
    return () => {
      eventBus.off('message', handleMessage);
    };
  }, []);
  
  return <div>接收到的消息:{message}</div>;
}

6. Ref转发

通过ref获取子组件的实例或DOM元素。

jsx
// 函数组件使用forwardRef
const Child = forwardRef((props, ref) => {
  const [value, setValue] = useState('');
  
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    },
    getValue: () => value,
    setValue: setValue
  }));
  
  const inputRef = useRef();
  
  return <input ref={inputRef} value={value} onChange={(e) => setValue(e.target.value)} />;
});

// 父组件
function Parent() {
  const childRef = useRef();
  
  const handleClick = () => {
    childRef.current.focus();
    console.log(childRef.current.getValue());
  };
  
  return (
    <div>
      <Child ref={childRef} />
      <button onClick={handleClick}>聚焦并获取值</button>
    </div>
  );
}

7. 通信方式选择指南

7.1 根据组件关系选择

组件关系推荐方案备选方案
父子组件Props + 回调函数Ref
兄弟组件状态提升Context、事件总线
跨层级组件Context API全局状态管理
全局共享Redux/ZustandContext API

8. 最佳实践

8.1 避免Props Drilling

jsx
// ❌ Props层层传递
function App() {
  const user = { name: 'John' };
  return <Level1 user={user} />;
}

function Level1({ user }) {
  return <Level2 user={user} />;
}

function Level2({ user }) {
  return <Level3 user={user} />;
}

// ✅ 使用Context
const UserContext = createContext();

function App() {
  const user = { name: 'John' };
  return (
    <UserContext.Provider value={user}>
      <Level1 />
    </UserContext.Provider>
  );
}

function Level3() {
  const user = useContext(UserContext);
  return <div>{user.name}</div>;
}

8.3 性能优化

jsx
// 使用React.memo避免不必要的重渲染
const TodoItem = React.memo(({ todo, onToggle }) => {
  return (
    <div onClick={() => onToggle(todo.id)}>
      {todo.text}
    </div>
  );
});

// 使用useCallback稳定回调函数
function TodoList({ todos }) {
  const [filter, setFilter] = useState('all');
  
  const handleToggle = useCallback((id) => {
    // 处理逻辑
  }, []);
  
  return (
    <div>
      {todos.map(todo => 
        <TodoItem key={todo.id} todo={todo} onToggle={handleToggle} />
      )}
    </div>
  );
}

总结

React组件通信的核心原则:

  1. 就近原则:优先使用最简单、最直接的通信方式
  2. 单向数据流:保持数据流向清晰可预测
  3. 组件解耦:减少组件间的直接依赖
  4. 性能考虑:避免不必要的重渲染
  5. 可维护性:选择易于理解和维护的方案

选择合适的通信方式需要考虑应用规模、组件关系、性能要求和团队偏好等多个因素。