React父子组件通信
在 React 中,数据流是单向的,即数据从父组件流向子组件,子组件无法直接修改父组件中的数据。这篇文章简单记录一下除了使用状态管理工具(如redux)以外,其他实现父子组件相互通信的方式。
1. 使用Props 传递
这是React中最常见和推荐的方式。父组件可以通过在子组件上设置属性来传递值。子组件可以通过 props 对象来访问这些属性。这种方式适用于父组件向子组件传递静态数据或者回调函数。
// 父组件
function ParentComponent() {
const data = 'Hello from parent';
return <ChildComponent data={data} />;
}
// 子组件
function ChildComponent(props) {
return <div>{props.data}</div>;
}
通过props可以实现父组件对子组件的传值,然后子组件可以通过callback函数来修改父组件的值
2. Callback 函数
父组件可以通过props将一个函数传递给子组件,在子组件中调用这个函数,并将需要传递的值作为参数传递给它。这种方式适用于父组件需要接收来自子组件的数据或者状态更新的情况。
// 父组件
function ParentComponent() {
const [data, setData] = useState('');
const handleDataChange = (newData) => {
setData(newData);
};
return (
<>
<ChildComponent onDataChange={handleDataChange} />
<div>Data from child: {data}</div>
</>
);
}
// 子组件
function ChildComponent(props) {
const handleClick = () => {
props.onDataChange('Hello from child');
};
return <button onClick={handleClick}>Change Data</button>;
3. 利用 Context 实现组件通信
如果应用中有一些全局的状态需要在多个组件中共享,你可以使用React的Context API。Context允许你在组件树中传递数据,而不需要手动通过props层层传递。这对于跨多层次的组件传递值非常有用。
import React, { createContext, useContext, useState } from 'react';
// 创建一个 Context
const MyContext = createContext();
// 父组件
function ParentComponent() {
const [data, setData] = useState('Hello from parent');
const updateData = (newData) => {
setData(newData);
};
return (
<MyContext.Provider value={{ data, updateData }}>
<ChildComponent />
</MyContext.Provider>
);
}
// 子组件
function ChildComponent() {
const { data, updateData } = useContext(MyContext);
const handleChange = () => {
updateData('Hello from child');
};
return (
<div>
<div>Data: {data}</div>
<button onClick={handleChange}>Change Data</button>
</div>
);
}
父组件中使用 useState 来管理 data 的状态,并通过 updateData 函数来更新这个状态。然后将 data 和 updateData 作为值传递给了Context的Provider组件。在子组件中通过 useContext 钩子来获取到Context的值,并通过调用 updateData 函数来更新Context的值。
这样,当子组件调用 updateData 函数时,会更新父组件中的状态,并通过 Context 将更新后的值传递给其他使用了该 Context 的子组件,从而实现了更改 Context 值的目的。
4. 使用Ref(引用)
Refs允许你在React组件中直接访问DOM元素或者子组件的实例。父组件可以创建一个ref,然后将它传递给子组件。子组件可以使用ref来访问父组件的DOM元素或者方法。父组件也可以使用子组件的ref来获取子组件的值或方法
import React, { useImperativeHandle, useRef } from 'react';
function ParentComponent() {
const parentRef = useRef();
const childRef = useRef();
const parentMethod = () => {
console.log('This is a method from parent component');
};
const handleClick = () => {
childRef.current.childMethod();
};
return (
<div ref={parentRef}>
<ChildComponent ref={childRef} parentRef={parentRef} parentMethod={parentMethod} />
<button onClick={handleClick}>Call Child Method</button>
</div>
);
}
// 子组件
const ChildComponent = React.forwardRef((props, ref) => {
const childMethod = () => {
console.log('This is a method from child component');
};
// 在这里可以访问父组件传递的 ref
// 通过 props.parentRef 访问 DOM 元素
// 通过 props.parentMethod 访问父组件的方法
// 注意:通过 ref.current 访问 DOM 元素或者通过 props 访问方法
useImperativeHandle(ref, () => ({
childMethod
}));
return (
<div>
<button onClick={() => props.parentRef.current && props.parentRef.current.scrollIntoView({ behavior: "smooth" })}>Scroll to parent</button>
<button onClick={props.parentMethod}>Call parent method</button>
</div>
);
});
在 React 中,当父组件通过 ref 访问子组件时,如果子组件没有使用 useImperativeHandle 来暴露某些方法或者属性,父组件是无法直接访问子组件中的方法或者属性的。这是因为在 React 中,父组件通过 ref 访问子组件得到的是子组件的实例,而子组件的内部方法和属性默认是不可访问的。
useImperativeHandle 的作用就是让你可以在函数组件中自定义暴露给父组件的实例值。通过 useImperativeHandle,你可以选择性地暴露一些子组件的方法或者属性给父组件,使得父组件可以通过 ref 来访问这些方法或者属性。
所以,如果希望父组件能够访问子组件中的某些方法或者属性,需要在子组件中使用 useImperativeHandle 来暴露这些方法或者属性给父组件。如果子组件没有使用 useImperativeHandle,父组件就无法直接访问子组件中的方法或者属性。
5. 与Vue的区别
虽然 React 和 Vue 都支持组件化开发,但它们在父子组件之间的数据传递上有一些不同之处:
- Props vs Props + Emit(Vue): 在 React 中,父组件通过 props 将数据传递给子组件,并且子组件无法直接修改父组件传递过来的 props。而在 Vue 中,父组件同样通过 props 将数据传递给子组件,但子组件可以通过 emit 方法向父组件发送事件,从而实现子组件向父组件传递数据。
- 组件通信方式的不同: 在 React 中,除了 props 传递外,还可以使用 Context API、回调函数、Ref 等方式进行组件通信。而在 Vue 中,除了 props + emit 和 refs 外,还可以使用 provide / inject、事件总线、Vuex、Pinia 等方式进行组件通信。
- 单向数据流 vs 双向数据绑定: 在 React 中,数据流是单向的,即数据从父组件流向子组件,子组件无法直接修改父组件中的数据。而在 Vue 中,数据流可以是双向的,父组件可以通过 props 向子组件传递数据,并且子组件可以通过修改 prop 的值来影响父组件中的数据。但是,虽然在 Vue 中可以直接来修改 prop 的值,但是这种做法并不提倡,还是需要使用例如Vue2的sync修饰符或者Vue3的v-model来在子组件中使用emit来修改。
转载自:https://juejin.cn/post/7350971151131525183