React 19中如何向Vue那样自定义状态和方法暴露给父组件。

文章目录

  • 前言
    • [一、什么是 useImperativeHandle?](#一、什么是 useImperativeHandle?)
      • [1.1 为什么需要 useImperativeHandle?](#1.1 为什么需要 useImperativeHandle?)
      • [1.2 基本语法](#1.2 基本语法)
    • [二、useImperativeHandle 的常见用法](#二、useImperativeHandle 的常见用法)
    • 注意点:
    • 总结

前言

在 React 的函数组件中,我们通常通过 props 将数据从父组件传递给子组件,而子组件通过状态(useState)和副作用(useEffect)来管理自身的行为。然而,在某些场景下,我们希望父组件能够直接调用子组件中的某些方法或访问其内部状态。这时,useImperativeHandle 就派上了用场。

本文将深入探讨 useImperativeHandle 的用法、原理以及最佳实践,帮助你更好地掌握这一强大的 Hook。


一、什么是 useImperativeHandle?

useImperativeHandle 是 React 提供的一个 Hook,用于自定义暴露给父组件的实例值。它通常与 forwardRef 一起使用,允许父组件通过 ref 访问子组件中定义的方法或属性。

1.1 为什么需要 useImperativeHandle?

  • 封装组件内部逻辑:允许子组件将内部方法或状态暴露给父组件,而不需要将所有细节公开。
  • 增强组件的可复用性:通过暴露特定的 API,父组件可以更灵活地控制子组件的行为。
  • 避免直接操作 DOM:虽然 React 鼓励声明式编程,但在某些场景下(如操作第三方库),可能需要直接操作 DOM 或组件实例。

1.2 基本语法

c 复制代码
	import React, { useImperativeHandle, forwardRef } from 'react';
	const ChildComponent = forwardRef((props, ref) => {
	  useImperativeHandle(ref, () => ({
	    customMethod: () => {
	      console.log('Custom method called!');
	    },
	  }));
	  return <div>Child Component</div>;
	});
	function ParentComponent() {
	  const childRef = React.useRef(null);
	  const handleClick = () => {
	    childRef.current?.customMethod();
	  };	
	  return (
	    <div>
	      <ChildComponent ref={childRef} />
	      <button onClick={handleClick}>Call Child Method</button>
	    </div>
	  );
	}

二、useImperativeHandle 的常见用法

3.1 暴露自定义方法

这是 useImperativeHandle最常见的用法。子组件可以暴露一些方法供父组件调用。

示例:自定义输入组件

c 复制代码
interface ChildRef {
  focus: () => void;
  getValue: () => string;
}
import React, { useImperativeHandle, useState, useRef } from "react";

// 子组件
const CustomInput = ({ ref }: { ref: React.Ref<ChildRef> }) => {
  const [value, setValue] = useState("");
  const iptRef = useRef<HTMLInputElement>(null);
  useImperativeHandle(ref, () => ({
    focus: () => {
      iptRef.current?.focus();
    },
    getValue: () => value,
  }));

  return (
    <input
      type="text"
      value={value}
      onChange={(e) => setValue(e.target.value)}
      ref={iptRef}
    />
  );
};

// 父组件
function ParentComponent() {
  const inputRef = useRef<ChildRef>(null);

  const handleFocus = () => {
    inputRef.current?.focus();
  };

 // 得到子组件的输入框中的值
  const handleGetValue = () => {
    console.log("Input value:", inputRef.current?.getValue());
  };

  return (
    <div>
      <CustomInput ref={inputRef} />
      <button onClick={handleFocus}>聚焦</button>
      <button onClick={handleGetValue}>得到输入框值</button>
    </div>
  );
}

export default ParentComponent;

3.2子组件封装的弹窗关闭方法暴露给外部

我们平常会封装一些组件,但改变组件状态通常由外部组件调用,这时我们就可以暴露方法给外部

示例:

c 复制代码
interface ChildRef {
  focus: () => void;
  getValue: () => string;
  close: () => void;
}
import React, { useImperativeHandle, useState, useRef } from "react";

// 子组件
const CustomInput = ({ ref }: { ref: React.Ref<ChildRef> }) => {
  const [value, setValue] = useState("");
  const iptRef = useRef<HTMLInputElement>(null);

  // 子组件的弹窗关闭
  const close = () => {
    console.log("子组件的弹窗关闭");
  };
  useImperativeHandle(ref, () => ({
    focus: () => {
      iptRef.current?.focus();
    },
    getValue: () => value,
    // 暴露子组件的弹窗关闭方法
    close: close,
  }));

  return (
    <input
      type="text"
      value={value}
      onChange={(e) => setValue(e.target.value)}
      ref={iptRef}
    />
  );
};

// 父组件
function ParentComponent() {
  const inputRef = useRef<ChildRef>(null);

  const handleFocus = () => {
    inputRef.current?.focus();
  };

  const handleGetValue = () => {
    console.log("Input value:", inputRef.current?.getValue());
  };

  return (
    <div>
      <CustomInput ref={inputRef} />
      <button onClick={handleFocus}>聚焦</button>
      <button onClick={handleGetValue}>得到输入框值</button>
      <button onClick={() => inputRef.current?.close()}>关闭弹窗</button>
    </div>
  );
}

export default ParentComponent;

简单效果展示:

注意点:

  • 1.React18还在用forwardRef进行接收值传递,在React 19直接解构出来ref,并赋值ts类型

总结

useImperativeHandle 是 React 中一个强大但容易被误用的 Hook。通过与 forwardRef 结合,它允许子组件自定义暴露给父组件的 API,从而实现更灵活的组件间通信。然而,使用时需要谨慎,避免滥用,保持组件的封装性和可维护性。

相关推荐
霸王蟹几秒前
React 19版本refs也支持清理函数了。
前端·javascript·笔记·react.js·前端框架·ts
繁依Fanyi6 分钟前
ColorAid —— 一个面向设计师的色盲模拟工具开发记
开发语言·前端·vue.js·编辑器·codebuddy首席试玩官
codelxy9 分钟前
vue引用cesium,解决“Not allowed to load local resource”报错
javascript·vue.js
Generalzy27 分钟前
学习!FastAPI
学习·sqlite·fastapi
ha204289419434 分钟前
c++学习之--- list
c语言·c++·学习·list
程序猿阿伟1 小时前
《社交应用动态表情:RN与Flutter实战解码》
javascript·flutter·react native
N_NAN_N1 小时前
程序设计语言----软考中级软件设计师(自用学习笔记)
笔记·学习
明似水1 小时前
Flutter 开发入门:从一个简单的计数器应用开始
前端·javascript·flutter
沐土Arvin1 小时前
前端图片上传组件实战:从动态销毁Input到全屏预览的全功能实现
开发语言·前端·javascript
ZHOU_WUYI2 小时前
React与Docker中的MySQL进行交互
mysql·react.js·docker