在 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>
);
}
关键点:
name和age是传递给子组件的数据。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函数,并通过onDataChangeprop 传递给子组件。 - 子组件在
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 的声明式原则。
- 应谨慎使用,仅用于
focus、scroll、触发动画等无法通过props轻松实现的场景。 - 优先考虑通过
props控制状态来实现。
总结:父子通信的核心模式
| 方向 | 方法 | 说明 |
|---|---|---|
| 父 → 子 | props | 传递数据、函数、JSX(children)。 |
| 子 → 父 | 回调函数 (Callback) | 父组件传递函数给子组件,子组件调用该函数并传参。 |
| 父 → 子 (命令式) | ref | 父组件直接调用子组件暴露的方法(非标准数据流,慎用)。 |
核心原则:
- 单向数据流:数据从父组件流向子组件。
- 状态提升:当多个组件需要共享状态时,将状态提升到它们最近的共同父组件中管理。
- 通过回调实现反向通信:子组件通过调用父组件传入的函数来“通知”父组件。
这种模式保证了数据流的清晰和可预测性,是 React 应用架构的基石。
THE END


