在 react 实际项目开发中,有时会需要在父组件中调用子组件方法,而且子组件是在循环中生成的,虽然这种做法不符合 react 的设计哲学,但是在开发中确实有这方面的需求,下面记录一下两种解决方法:
第一种方法是使用
useRef/useImperativeHandle/forwardRef
:
useRef
初始化为数组
childRefs.current[item.id] = ref;
forwardRef
包一下,(使用
connect
包裹的子组件需要添加
{ forwardRef: true }
,见
这里
)
useImperativeHandle
导出子组件的方法,注意子组件的第二个参数
下面是最简化的代码和相关注释:
// 父组件:
import React, { useRef } from 'react';
import ChildComp from './ChildComp';
function App() {
const arr = [
id: 1,
name: '🍎',
id: 2,
name: '🍌',
id: 3,
name: '🍊',
// useRef 初始化为数组
const childRefs = useRef<any>([]);
return (
<div className="App">
{arr.map((item) => (
<ChildComp
// ⚠️注意 ref 有可能为空 所以在此处加一个判断
ref={(ref) => {
if (ref) {
childRefs.current[item.id] = ref;
data={item}
key={item.id}
<button
onClick={() => {
childRefs.current &&
childRefs.current.forEach((childRef: any) => {
// 调用所有子组件的方法
childRef.exportChildMethod();
// 获取所有子组件的值
console.log(childRef.exportChildValue());
// 获取某个子组件的值
console.log(
childRefs.current && childRefs.current[1].exportChildValue()
调用子组件的方法
</button>
</div>
export default App;
// 子组件
import React, { forwardRef, useState, useImperativeHandle } from 'react';
// 使用forwardRef包裹的组件可以获得一个ref的参数
const Child = (props: any, ref: any) => {
const { data } = props;
const [childNum, setChildNum] = useState<number>(0);
// 在useImperativeHandle中导出被父组件使用的方法
useImperativeHandle(ref, () => ({
exportChildMethod: () => {
childMethod();
exportChildValue: () => {
return childNum;
const childMethod = () => {
console.log(`log from child--${data.name}`);
return (
<p>{data.name}</p>
<p>{childNum}</p>
<button
onClick={() => {
setChildNum(childNum + 1);
child add
</button>
</div>
// 需要使用forwardRef包裹
export default forwardRef(Child);
第二种方法是不使用 useRef/forwardRef/useImperativeHandle
, 原理就是 props 改变触发子组件的方法:
父组件设置一个毫秒数ms
为空
父组件触发动作,设置毫秒数ms
为new Date().getTime()
把 ms
传到子组件中
子组件在useEffect
中依赖 ms
的值,ms
改变触发子组件内的函数
import React from 'react';
import ChildComp from './ChildComp';
function App() {
const arr = [
id: 1,
name: '🍎',
id: 2,
name: '🍌',
id: 3,
name: '🍊',
const [ms, setMs] = useState<number | undefined>();
const emitChildMethod = () => {
setMs(new Date().getTime());
return (
<div className="App">
{arr.map((item) => (
<ChildComp ms={ms} data={item} key={item.id} />
<button onClick={emitChildMethod}>调用子组件的方法</button>
</div>
</div>
export default App;
// 子组件
import React, { useState, useEffect } from 'react';
const Child = (props: any) => {
const { data } = props;
const [childNum, setChildNum] = useState<number>(0);
useEffect(() => {
// 此处需要做一下判断
if (ms) {
childMethod();
}, [ms]);
const childMethod = () => {
console.log(`log from child--${data.name}`);
return (
<p>{data.name}</p>
<p>{childNum}</p>
<button
onClick={() => {
setChildNum(childNum + 1);
child add
</button>
</div>
export default Child;
这两种方法都是不得已而为止的办法,破坏了单项数据流的设计模式
也不利于代码调试和业务梳理
但是在老代码的改造和添加新功能方面有一定优势