面试题:React 中,父子组件如何进行通信?

在 React 中,组件通信是构建复杂应用的核心。父子组件通信是最基础、最常见的方式,其核心原则是 “数据单向流动” —— 数据从父组件流向子组件。

1. 父组件向子组件传递数据:通过 props

这是最直接和最主要的方式。父组件通过在子组件标签上定义属性,将数据和函数作为 props 传递给子组件。

示例

// 子组件
function ChildComponent({ name, age, onGreet }) {
  return (
    <div>
      <p>姓名: {name}</p>
      <p>年龄: {age}</p>
      {/* 子组件调用父组件传递的函数 */}
      <button onClick={() => onGreet(name)}>打招呼</button>
    </div>
  );
}

// 父组件
function ParentComponent() {
  const [userName, setUserName] = useState('Alice');
  const [userAge, setUserAge] = useState(25);

  // 定义一个函数,将由子组件调用
  const handleGreet = (name) => {
    alert(`Hello, ${name}!`);
  };

  return (
    <div>
      {/* 父组件通过 props 向子组件传递数据和函数 */}
      <ChildComponent 
        name={userName} 
        age={userAge} 
        onGreet={handleGreet} 
      />
    </div>
  );
}

关键点

  • nameage 是传递给子组件的数据。
  • onGreet 是传递给子组件的函数引用。子组件可以调用这个函数,从而实现“子组件向父组件通信”的效果(见下一点)。

2. 子组件向父组件传递数据:通过回调函数 (Callback)

子组件无法直接修改父组件的状态。要实现“子到父”的通信,父组件需要先传递一个函数作为 props 给子组件,然后子组件在需要时调用这个函数,并将数据作为参数传回。

示例(接上例):

// 子组件:接收并调用父组件的回调函数
function ChildComponent({ name, age, onGreet, onDataChange }) {
  const [localValue, setLocalValue] = useState('');

  const handleChange = (e) => {
    const value = e.target.value;
    setLocalValue(value);
    // 子组件通过调用回调函数,将数据传回给父组件
    onDataChange(value); 
  };

  return (
    <div>
      <p>姓名: {name}</p>
      <p>年龄: {age}</p>
      <input 
        value={localValue} 
        onChange={handleChange} 
        placeholder="输入一些内容"
      />
      <button onClick={() => onGreet(name)}>打招呼</button>
    </div>
  );
}

// 父组件:定义回调函数
function ParentComponent() {
  const [userName, setUserName] = useState('Alice');
  const [userAge, setUserAge] = useState(25);
  const [childData, setChildData] = useState(''); // 接收子组件传来的数据

  const handleGreet = (name) => {
    alert(`Hello, ${name}!`);
  };

  // 回调函数:接收子组件传来的数据
  const handleDataChange = (data) => {
    setChildData(data);
    console.log('来自子组件的数据:', data);
  };

  return (
    <div>
      <ChildComponent 
        name={userName} 
        age={userAge} 
        onGreet={handleGreet}
        onDataChange={handleDataChange} // 传递回调函数
      />
      <p>父组件收到: {childData}</p>
    </div>
  );
}

关键点

  • 父组件定义 handleDataChange 函数,并通过 onDataChange prop 传递给子组件。
  • 子组件在 onChange 事件中调用 onDataChange(value),将输入的值传回父组件。
  • 父组件的 handleDataChange 函数执行,更新自己的状态 childData

3. 使用 ref 进行命令式通信(不推荐用于常规数据流)

虽然 props 和回调是标准方式,但有时父组件需要直接调用子组件的方法(如触发子组件的焦点、重置表单等)。

示例

// 子组件:暴露一个方法
const TextInput = React.forwardRef((props, ref) => {
  const inputRef = useRef();

  // 暴露给父组件的方法
  const focusInput = () => {
    inputRef.current.focus();
  };

  // 使用 useImperativeHandle 暴露特定方法
  useImperativeHandle(ref, () => ({
    focusInput
  }));

  return <input ref={inputRef} type="text" />;
});

// 父组件:使用 ref 调用子组件方法
function Parent() {
  const childRef = useRef();

  const handleClick = () => {
    // 父组件直接调用子组件的方法
    childRef.current.focusInput();
  };

  return (
    <div>
      <TextInput ref={childRef} />
      <button onClick={handleClick}>聚焦输入框</button>
    </div>
  );
}

注意

  • 这种方式是命令式的,打破了 React 的声明式原则。
  • 应谨慎使用,仅用于 focusscroll、触发动画等无法通过 props 轻松实现的场景。
  • 优先考虑通过 props 控制状态来实现。

总结:父子通信的核心模式

方向方法说明
父 → 子props传递数据、函数、JSX(children)。
子 → 父回调函数 (Callback)父组件传递函数给子组件,子组件调用该函数并传参。
父 → 子 (命令式)ref父组件直接调用子组件暴露的方法(非标准数据流,慎用)。

核心原则

  • 单向数据流:数据从父组件流向子组件。
  • 状态提升:当多个组件需要共享状态时,将状态提升到它们最近的共同父组件中管理。
  • 通过回调实现反向通信:子组件通过调用父组件传入的函数来“通知”父组件。

这种模式保证了数据流的清晰和可预测性,是 React 应用架构的基石。

THE END
喜欢就支持一下吧
点赞11 分享