react 父子组件通信 子 直接到父, 父 forwardref子

React核心概念:单向数据流(Unidirectional Data Flow)

React 中数据的流动像瀑布一样,只能从上层组件(父组件)流向下层组件(子组件)

子组件无法直接反向修改父组件的数据,只能通过父组件传递的回调函数来"通知"父组件修改数据。

(他即使子组件调用了,也只是通知📢没法修改)

【父调子】

useImperativeHandle

这个东西是父组件想要用一下子的,就比较难,因为有门槛不希望你这样做。

分三步:

  1. 子组件用 forwardRef 包裹 :允许父组件传递 ref 进来。

  2. 子组件内用 useImperativeHandle:定义要暴露给父组件的内容。

  3. 父组件通过 ref 调用子组件的方法 :比如 ref.current.focus()

    const ChildInput = forwardRef((props, ref) => {

    useImperativeHandle(ref, () => ({
    con: () => {
    console.log('122aaa ');
    },
    }

父组件那边 childRef.current

<ChildInput ref={childRef} />

那还有,如果你是用ts,总是报错 ref.current.xxx,那是因为你没有定义好类型。

复制代码
// 定义子组件暴露给父组件的方法类型
export type ChildInputHandle = {
  focus: () => void;
  clear: () => void;
};

然后也要forwardRef<ChildInputHandle>

父组件里也类型

复制代码
  const childRef = useRef<ChildInputHandle>(null);

最后if xxx .current再操作

关键点解释:

  1. 为什么用 forwardRef

    React 默认不允许组件直接接收 ref(除非是原生 DOM 元素),所以需要用 forwardRef 包裹子组件,让它能接收父组件传递的 ref

  2. useImperativeHandle 的作用

    它像一个"过滤器",决定父组件通过 ref 能访问子组件的哪些方法/属性。比如上面的例子中,父组件只能调用 focusclear,而无法直接操作子组件的 DOM(比如改输入框的值)。

  3. 调用时的注意事项

    父组件必须通过 ref.current.方法名() 调用,且要确保子组件已经正确暴露了该方法(否则会报错 xxx is not a function)。

子组件使用父亲的方法

那如果是是子组件去用父组件用的,那么就会简单一点,直接传props就行了

  1. 父组件给子组件一个"对讲机"(回调函数)
  2. 子组件需要传数据时,按对讲机说话(调用回调函数并传数据)
  3. 父组件自动响应 :父组件收到数据后,更新自己的状态(比如 setState),触发重新渲染

如果发现父组件频繁调用子组件方法,很可能你的组件结构需要重构(比如状态提升到父组件)useImperativeHandle 应当是最后的选择,而不是首选方案

关键点解释:

  1. 数据流向
    • 父 → 子 :父组件通过 props(如 value={count})将数据传递给子组件。
    • 子 → 父 :子组件通过调用父组件传递的回调函数(如 onChildClick())触发父组件更新数据。
  2. 为什么说它是单向的?
    • 父组件的数据变化会自动流向子组件(子组件的 value 会更新)。
    • 子组件不能直接修改父组件的 count,只能通过回调函数"请求"父组件自己修改。
  3. "从大到小"的层级
    • 这里的"大"指的是组件层级更高(如父组件),数据从高层级流向低层级(子组件)。

附 父调子的react代码

复制代码
// 父组件 Parent.tsx
import React, { useRef } from 'react';
import ChildInput, { ChildInputHandle } from './childInput';

export function Parent() {
  // 明确指定 ref 类型为 ChildInputHandle | null
  const childRef = useRef<ChildInputHandle>(null);

  const handleClick = () => {
    if (childRef.current) {
      childRef.current.focus(); // TS 不再报错
    }
  };

  return (
    <div>
      <ChildInput ref={childRef} />
      <button onClick={handleClick}>Focus</button>
    </div>
  );
}

// 子组件 ChildInput.tsx
import React, { forwardRef, useImperativeHandle, useRef } from 'react';

// 定义子组件暴露给父组件的方法类型
export type ChildInputHandle = {
  focus: () => void;
  clear: () => void;
};

const ChildInput = forwardRef<ChildInputHandle>((props, ref) => {
  const inputRef = useRef<HTMLInputElement>(null);

  useImperativeHandle(ref, () => ({
    focus: () => inputRef.current?.focus(),
    clear: () => {
      if (inputRef.current) inputRef.current.value = '';
    },
  }));

  return <input ref={inputRef} />;
});
ChildInput.displayName = 'childInput';
export default ChildInput;
相关推荐
水银嘻嘻1 小时前
12 web 自动化之基于关键字+数据驱动-反射自动化框架搭建
运维·前端·自动化
it_remember1 小时前
新建一个reactnative 0.72.0的项目
javascript·react native·react.js
小嘟嚷ovo2 小时前
h5,原生html,echarts关系网实现
前端·html·echarts
十一吖i2 小时前
Vue3项目使用ElDrawer后select方法不生效
前端
只可远观2 小时前
Flutter目录结构介绍、入口、Widget、Center组件、Text组件、MaterialApp组件、Scaffold组件
前端·flutter
周胡杰2 小时前
组件导航 (HMRouter)+flutter项目搭建-混合开发+分栏效果
前端·flutter·华为·harmonyos·鸿蒙·鸿蒙系统
敲代码的小吉米3 小时前
前端上传el-upload、原生input本地文件pdf格式(纯前端预览本地文件不走后端接口)
前端·javascript·pdf·状态模式
是千千千熠啊3 小时前
vue使用Fabric和pdfjs完成合同签章及批注
前端·vue.js
da-peng-song3 小时前
ArcGIS Desktop使用入门(二)常用工具条——数据框工具(旋转视图)
开发语言·javascript·arcgis
九月TTS3 小时前
TTS-Web-Vue系列:组件逻辑分离与模块化重构
前端·vue.js·重构