react 通过ref调用子组件的方法

背景

父组件内引入了一个弹窗组件,弹窗组件使用了完全内聚的开发方法;

实现思路

父组件内通过ref获取的子组件,通过current调用子组件的方法,子组件需要通过forwardRef进行“包装”导出,通过useImperativeHandle暴露可以被current使用的方法;

父组件代码

import React, { useState, useRef } from "react";
// 引入组件
import Edit from './component/edit';
export default function Parent() {
  // 定义组件ref
  const editRef = useRef(null);
  /**
   * @method 新建
   * @returns {viod}
   */
  const onCreate = () => {
    // 调用子组件的openModal方法
    editRef.current.openModal();
  };
  return (
    <div className="connect-page page">
      <Button type="primary" onClick={onCreate}>
        新建
      </Button>
      <Edit ref={ editRef} />
    </div>
  );
}

子组件代码

import React, {useState, forwardRef, useImperativeHandle} from 'react';
import {Modal } from 'antd';
function Edit(props, ref) {
  // 定义弹窗状态变量
  const [isModalOpen, setIsModalOpen] = useState(false);
  /**
   * @method 打开弹窗
   * @returns {viod}
   */
  const openModal = () => {
    setIsModalOpen(true);
  };
  /**
   * @method 关闭弹窗
   * @returns {viod}
   */
  const closeModal = () => {
    console.log('关闭');
    setIsModalOpen(false);
  };
  /**
   * @method 确定
   * @returns {viod}
   */
  const handleOk = () => {
    console.log('确定');
    closeModal();
  };
  /**
   * @method 取消
   * @returns {viod}
   */
  const handleCancel = () => {
    console.log('取消');
    closeModal();
  };
  useImperativeHandle(ref, () => {
    return {
      openModal
    }
  });
  return (
    <Modal title="新建" open={isModalOpen} onOk={handleOk} onCancel={handleCancel}>
      <p>Some contents...</p>
      <p>Some contents...</p>
      <p>Some contents...</p>
    </Modal>
  )
}
export default forwardRef(Edit);

踩坑

1. Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

解决方案:子组件使用forwardRef进行“包装”后进行导出;

// 子组件
export default forwardRef(需要导出的组件);

2. 父组件找不到子组件方法

解决方案:子组件内使用useImperativeHandle对方法进行暴露;

import React, {
  // 其他引入...
  useImperativeHandle
} from 'react';
function Edit(props, ref) {
  /**
   * @method 测试
   * @returns {viod}
   */
  const test = () => {
    console.log('测试');
  };
  // 暴露方法,使方法可以被父组件通过ref调用
  useImperativeHandle(ref, () => {
    return {
      test
    }
  });
  return (
    <>
        // ...
    </>
  )
}
export default forwardRef(Edit);

React 中,父组件调用子组件方法是一种常见的组件通信方式,尤其是在需要父组件主动触发子组件行为的场景中。根据组件类型(类组件或函数组件)和使用方式,主要有以下几种实现方式: ### 类组件中通过 `React.createRef` 调用子组件方法 在类组件中,可以使用 `React.createRef()` 创建一个引用,并将其绑定到子组件上,从而允许父组件直接调用子组件方法。这种方式适用于子组件需要暴露某些方法给父组件调用的情况。 ```javascript class Parent extends React.Component { constructor(props) { super(props); this.childRef = React.createRef(); } handleCallChildMethod = () => { this.childRef.current.sendMessage(); // 调用子组件方法 }; render() { return ( <div> <Child ref={this.childRef} /> <button onClick={this.handleCallChildMethod}>调用子组件方法</button> </div> ); } } ``` 子组件定义如下: ```javascript class Child extends React.Component { sendMessage = () => { console.log('子组件方法调用'); }; render() { return <div>子组件</div>; } } ``` 这种方式在类组件中非常直观且易于实现[^1]。 --- ### 函数组件中通过 `useImperativeHandle` 和 `forwardRef` 暴露方法 在函数组件中,由于没有类实例,不能直接通过 `ref` 访问内部方法,因此需要结合 `React.forwardRef` 和 `useImperativeHandle` 来暴露子组件方法。 ```javascript const Child = React.forwardRef((props, ref) => { const sendMessage = () => { console.log('子组件方法调用'); }; React.useImperativeHandle(ref, () => ({ sendMessage })); return <div>子组件</div>; }); ``` 父组件调用方式: ```javascript function Parent() { const childRef = React.useRef(); const handleCallChildMethod = () => { childRef.current.sendMessage(); // 调用子组件方法 }; return ( <div> <Child ref={childRef} /> <button onClick={handleCallChildMethod}>调用子组件方法</button> </div> ); } ``` 这种方式使得函数组件也可以支持类似类组件的 `ref` 调用方式,同时保持组件封装性[^4]。 --- ### 子组件主动将自身实例传递给父组件 另一种方式是在子组件挂载时,通过 props 回调将自身(`this`)传递给父组件,从而让父组件可以保存子组件的引用并调用方法。 ```javascript class Child extends React.Component { componentDidMount() { this.props.onChildEvent(this); } sendMessage = () => { console.log('子组件方法调用'); }; render() { return <div>子组件</div>; } } ``` 父组件定义: ```javascript class Parent extends React.Component { childInstance = null; handleChildEvent = (child) => { this.childInstance = child; }; handleCallChildMethod = () => { if (this.childInstance) { this.childInstance.sendMessage(); } }; render() { return ( <div> <Child onChildEvent={this.handleChildEvent} /> <button onClick={this.handleCallChildMethod}>调用子组件方法</button> </div> ); } } ``` 这种方式在某些场景下可以简化通信流程,但需要注意生命周期的管理[^2]。 --- ### 使用 Props 传递回调函数 虽然这种方式主要用于子组件调用父组件方法,但在某些情况下也可以通过反向调用实现父组件控制子组件的行为。例如,父组件通过 props 传递一个函数,子组件在特定时机调用该函数,从而触发父组件的逻辑。 ```javascript class Parent extends React.Component { handleChildAction = () => { console.log('父组件响应子组件动作'); }; render() { return <Child onAction={this.handleChildAction} />; } } ``` 子组件: ```javascript class Child extends React.Component { triggerAction = () => { this.props.onAction(); }; render() { return ( <div> <button onClick={this.triggerAction}>触发父组件方法</button> </div> ); } } ``` 此方式更适用于子组件主动通知父组件的情况,而不是父组件主动调用子组件方法[^3]。 --- ### 总结对比 | 方法 | 使用场景 | 特点 | |------|----------|------| | `React.createRef()`(类组件) | 子组件主动暴露方法给父组件调用 | 直观、符合 React 组件间通信习惯 | | `forwardRef + useImperativeHandle`(函数组件) | 函数组件中暴露方法给父组件调用 | 支持函数组件,保持封装性 | | 子组件传递 `this` 给父组件 | 父组件需持有子组件实例 | 简化调用流程,需注意生命周期 | | Props 传递回调函数 | 子组件通知父组件事件 | 更函数式,便于管理多个子组件 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值