React+TypeScript父子组件通讯

通过本文的阅读,读者将学会如何在 React 中通过高级特性来协同父子组件间的交互。具体来说,将掌握如何利用 forwardRefuseImperativeHandle 钩子在子组件中暴露多个 DOM 节点或方法给父组件。还将了解如何在TS类型安全的前提下,准确地传递和使用这些方法和节点。文章进一步介绍了如何从子组件调用父组件中的方法,以实现组件间的逻辑交互和协作。通过这些技能,读者能够实现更加精细的组件控制和事件处理,提升 React 应用的可维护性和用户体验。

子组件将 DOM 节点暴露给父组件

在子组件中使用 forwardRef 将组件包裹,将允许组件使用 ref 将 DOM 节点暴露给父组件。

子组件:

tsx 复制代码
import {forwardRef} from "react";

// 子组件的Props类型
interface ChildrenProps {
  title: string;
}

export const Children = forwardRef<HTMLInputElement, ChildrenProps>((props, ref) => {
  return (
    <div>
      <div>{props.title}</div>
      <div><input type="text" ref={ref}/></div>
    </div>
  );
});

forwardRef 泛型中接收两个参数:

  • 第一个参数是 ref 的类型,上述示例中由于 ref 绑定到了 input 元素节点上,所以类型为 HTMLInputElement
  • 第二个参数是 props 的类型,上述示例中我们接收 title

父组件:

在父组件中调用之前我们创建好的子组件,即可传递 titleref 属性了

tsx 复制代码
import {Children} from "@/pages/Demo/Children.tsx";
import {useEffect, useRef} from "react";

export const Parent = () => {
  const childrenRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    console.log(childrenRef.current)
  }, []);

  return (
    <div className={'container-background'}>
      <Children title='辰火流光' ref={childrenRef}/>
    </div>
  );
};

在浏览器中预览:

配合 useImperativeHandle

useImperativeHandleReact Hook 之一。使用 useImperativeHandle,可以让子组件内部显式地指定通过 ref 暴露给父组件的值。

useImperativeHandle 通常和 forwardRef 一起使用,以允许函数组件接收 ref 属性。

使用场景:

  1. 当我们需要向父组件暴露子组件的某些特定方法时,而不是整个 DOM 节点或组件实例。
  2. 当我们希望能够控制父组件对子组件 ref 的访问模式,包括隐藏一些内部状态或不希望暴露的方法。

假设子组件中有两个input需要在暴露给外部

子组件代码:

tsx 复制代码
import React, { forwardRef, useRef, useImperativeHandle } from "react";

// 子组件的Props类型
interface ChildrenProps {
  title: string;
}

// 子组件暴露给父组件调用的方法的类型
export interface ChildrenRefs {
  inputRef1: React.RefObject<HTMLInputElement>;
  inputRef2: React.RefObject<HTMLInputElement>;
}

export const Children = forwardRef<ChildrenRefs, ChildrenProps>((props, ref) => {
  // 创建多个ref
  const inputRef1 = useRef<HTMLInputElement>(null);
  const inputRef2 = useRef<HTMLInputElement>(null);

  // 使用useImperativeHandle暴露给父组件的接口
  useImperativeHandle(ref, () => ({
    // 暴露这些input的ref
    inputRef1,
    inputRef2,
  }));

  return (
    <div>
      <div>{props.title}</div>
      <div><input type="text" placeholder='input1' ref={inputRef1}/></div>
      <div><input type="text" placeholder='input2' ref={inputRef2}/></div>
    </div>
  );
});

父组件代码:

tsx 复制代码
import {Children, ChildrenRefs} from "@/pages/Demo/Children.tsx";
import {useEffect, useRef} from "react";

export const Parent = () => {
  const childrenRef = useRef<ChildrenRefs>(null);

  useEffect(() => {
    console.log(childrenRef.current?.inputRef1.current)
    console.log(childrenRef.current?.inputRef2.current)
  }, []);

  return (
    <div className={'container-background'}>
      <Children title='辰火流光' ref={childrenRef}/>
    </div>
  );
};

假设我们不想暴露出整个 <input> DOM 节点,只想要其中 focus 方法,只需要修改 useImperativeHandle 返回你想要父组件去调用的方法:

tsx 复制代码
useImperativeHandle(ref, () => ({
    inputRef1Focus(){
      inputRef1.current?.focus()
    },
    inputRef2,
}));

父组件修改以下代码:

tsx 复制代码
useEffect(() => {
    console.log(childrenRef.current?.inputRef1Focus())
    console.log(childrenRef.current?.inputRef2.current)
}, []);

子组件调用父组件的方法

只需要在父组件中传递该方法到子组件中即可

父组件:

tsx 复制代码
import {Children} from "@/pages/Demo/Children.tsx";

export const Parent = () => {
  // 父组件的方法
  const parentMethod = () => {
    console.log('父组件方法被调用')
  }

  return (
    <div className={'container-background'}>
      {/*增加传递父组件的方法*/}
      <Children onParentMethod={parentMethod} />
    </div>
  );
};

子组件:

tsx 复制代码
// 子组件的Props类型
interface ChildrenProps {
  onParentMethod: () => void;
}

export const Children = (props: ChildrenProps) => {

  const handleButtonClick=()=>{
    props.onParentMethod();
  }

  return <button onClick={handleButtonClick}>调用父组件的方法</button>
};

总结

本文介绍了如何在 React 中处理父子组件间的交互,具体包括如何通过 forwardRefuseImperativeHandle 在子组件中暴露 DOM 节点或特定方法给父组件,并增强了类型安全性。同时,我们也深入探讨了如何在子组件中调用父组件的方法,以便实现复杂的逻辑交互。

对于新手来说,理解父子组件间的交互可能稍有复杂,但一旦掌握,你会发现 React 在组件间的复用和逻辑控制方面的强大之处。实践中,这些特性能大大提高代码的可读性,可维护性甚至可测试性。

希望通过本文的学习,你已经足够熟练掌握了这些内容,能够在自己的项目中流畅地使用和掌握这些知识,构建出更加健壮且易于维护的 React 应用,提升团队的开发效率和代码质量。总的来说,理解并掌握这些特性是每一个 React 开发者的必备知识点和技能,希望本文能为你的学习之路提供帮助。

相关推荐
谢尔登4 小时前
Monorepo 架构
前端·arcgis·架构
栀秋6664 小时前
你会先找行还是直接拍平?两种二分策略你Pick哪个?
前端·javascript·算法
漂流瓶jz4 小时前
PostCSS完全指南:功能/配置/插件/SourceMap/AST/插件开发/自定义语法
前端·javascript·css
xhxxx4 小时前
传统工具调用太痛苦?LangChain 一键打通 LLM 与真实世界
前端·langchain·llm
南山安4 小时前
LangChain学习:Memory实战——让你的大模型记住你
前端·javascript·langchain
BD_Marathon5 小时前
Promise基础语法
开发语言·前端·javascript
BOF_dcb5 小时前
网页设计DW
前端
千寻girling5 小时前
计算机组成原理-全通关源码-实验(通关版)---头歌平台
前端·面试·职场和发展·typescript·node.js
karshey6 小时前
【前端】解决:点击一个button,发现不触发点击事件
前端
用泥种荷花6 小时前
【前端学习AI】Function Calling
前端