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 的开发实践中,父组件调用子组件方法是一种常见的需求场景。以下是几种主流的实现方式及其适用范围: #### 方法一:使用 `React.createRef` 调用子组件方法 对于类组件而言,可以通过创建一个 Ref 来访问子组件实例,并进而调用其公开的方法。这种方式简单直观,适用于需要直接操作子组件的情况。 ```javascript // ParentComponent.js import React from 'react'; import ChildComponent from './ChildComponent'; class ParentComponent extends React.Component { constructor(props) { super(props); this.childRef = React.createRef(); } callChildMethod = () => { if (this.childRef.current) { this.childRef.current.someChildMethod(); // 调用子组件方法 } }; render() { return ( <div> <button onClick={this.callChildMethod}>Call Child Method</button> <ChildComponent ref={this.childRef} /> </div> ); } } export default ParentComponent; // ChildComponent.js import React from 'react'; class ChildComponent extends React.Component { someChildMethod = () => { console.log('Child method was called'); }; render() { return <div>Child Component</div>; } } ``` 此方法利用了 `React.createRef()` 创建了一个 Ref 实例[^1],并通过它获取子组件实例上的公共方法。 --- #### 方法二:通过 Props 回调机制间接调用 另一种常见的方式是在子组件挂载完成后,将其自身的引用传递给父组件。这样可以使得父组件能够存储该引用以便后续调用。 ```javascript // Child.js import React, { Component } from 'react'; class Child extends Component { componentDidMount() { this.props.onChildEvent(this); // 将子组件实例传递给父组件 } sendMessage = () => { console.log('Message sent from child component'); }; render() { return <span>Child</span>; } } export default Child; // Parent.js import React, { Component } from 'react'; import Child from './Child'; class Parent extends Component { handleChildInstance(childInstance) { this.childInstance = childInstance; } callChildMethod = () => { if (this.childInstance && typeof this.childInstance.sendMessage === 'function') { this.childInstance.sendMessage(); // 调用子组件方法 } }; render() { return ( <div> <button onClick={this.callChildMethod}>Call Child Method</button> <Child onChildEvent={this.handleChildInstance.bind(this)} /> </div> ); } } export default Parent; ``` 这种模式允许父组件保存对子组件实例的引用,从而可以在任何时间点调用子组件暴露的方法[^3]。 --- #### 方法三:借助 Context API 进行跨层通信 当涉及到更深层次嵌套或者多个兄弟组件之间的交互时,Context 提供了一种优雅的方式来共享状态或行为逻辑。在这种情况下,父组件可以通过上下文向子孙节点广播自己的能力,而无需显式地逐级传递 props。 ```javascript // Context.js import React from 'react'; const MethodContext = React.createContext(); // ChildComponent.js import React from 'react'; import { MethodContext } from './Context'; class ChildComponent extends React.Component { render() { return ( <MethodContext.Consumer> {(callParentMethod) => ( <button onClick={() => callParentMethod()}> Call Parent Method </button> )} </MethodContext.Consumer> ); } } export default ChildComponent; // ParentComponent.js import React from 'react'; import ChildComponent from './ChildComponent'; import { MethodContext } from './Context'; class ParentComponent extends React.Component { handleParentMethod = () => { console.log('Parent method called from child'); }; render() { return ( <MethodContext.Provider value={this.handleParentMethod}> <ChildComponent /> </MethodContext.Provider> ); } } export default ParentComponent; ``` 这里展示了如何通过定义自定义上下文来简化父子间以及深层结构间的通讯流程[^2]。 --- #### 方法四:函数组件中的 Imperative Handle 如果项目主要基于函数组件构建,则可考虑结合 hooks 如 `useImperativeHandle` 和 `forwardRef` 来达到相同效果。 ```javascript // Child.js import React, { forwardRef, useImperativeHandle } from 'react'; const Child = forwardRef((props, ref) => { useImperativeHandle(ref, () => ({ func: () => { console.log('Function inside the child component.'); }, })); return <p>This is a functional child.</p>; }); export default Child; // Parent.js import React, { useRef } from 'react'; import Child from './Child'; const Parent = () => { const childRef = useRef(null); const handleClick = () => { if (childRef.current?.func) { childRef.current.func(); // 调用子组件方法 } }; return ( <> <button onClick={handleClick}>Invoke Child Function</button> <Child ref={childRef} /> </> ); }; export default Parent; ``` 这段代码片段说明了即使在函数式编程范式下也能灵活运用 imperative APIs 完成特定任务[^4]。 --- ### 总结 每一种技术都有各自的优势和局限性,在实际应用过程中应根据具体业务需求选择最合适的解决方案。通常来说,推荐优先尝试声明式的 prop 或 state 更新策略;只有确实必要才采用命令式的 refs 接口。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值