引入:如何调用函数式组件内部的方法

对于 React 中需要强制修改子组件的情况,React 提供了 Refs 这种解决办法,使得我们可以操作底层 DOM 元素或者自定的 class 组件实例。除此之外,文档(v17.0.1)对函数式组件另有描述:

不能在函数式组件上使用ref属性,因为他们没有实例

在函数式组件和 Hooks 大面积普及的现在,这个特性没有完全对标 class 组件,令人疑惑。不过经过一阵探索和请教,发现确实是有对应的解决方案的:

useImperativeHandle

结合 React.forwardRefuseImperativeHandle 文档应该就能明白是如何使用的。

简而言之就是可以在函数式组件上使用 ref,通过useImperativeHandle这个hook可以指定暴露给父组件的值和函数。

案例:

修改子组件Counter中的值, 达到重置count的目的:

export default function App() {
return (
<div>
<button>reset</button>
<Counter />
</div>
);
}
/** -------------------------------------- */
function Counter() {
const [count, setCount] = useState(0);
function increment() {
setCount(count + 1);
}
return (
<div>
<hr />
<span>{count}</span>
<button onClick={increment}>+1</button>
</div>
);
}

对于这个案例,将count这个state往上提一层到 App 组件中是比较合适的,但是在这里重点讨论操作子组件

使用useImperativeHandle,修改代码:

export default function App() {
const counterRef = useRef();
function reset() {
counterRef.current?.resetCount();
}
return (
<div style={{ padding: 10 }}>
<button onClick={reset}>reset</button>
<MyCounter ref={counterRef} />
</div>
);
}
/** -------------------------------------- */
function Counter(props, ref) {
const [count, setCount] = useState(0);
useImperativeHandle(ref, () => ({
resetCount: resetCount,
}));
function resetCount() {
setCount(0);
}
function increment() {
setCount(count + 1);
}
return (
<div>
<hr />
<span>{count}</span>
<button onClick={increment}>+1</button>
</div>
);
}
const MyCounter = React.forwardRef(Counter);

重点是useImperativeHandle中定义了resetCount,以及使用React.forward获取 ref,在App组件中为MyCounter中定义ref属性,然后就可以在外部父组件中使用通过ref调用子组件的resetCount方法。

到这里,实际上已经达到了和classref对等的效果。通过给函数式组件设置 ref 并调用组件的方法是可行的,useImperativeHandle除了添加方法,也可以指定值暴露出去。

函数式组件的Ref是什么

将 ref 设置到 HTML 元素上,获取的是对应的DOM元素,如span:

设置到 class 组件上,获取的是 class 组件实例:

设置到函数式组件上的时候,获取的是一个包含可变值或函数的对象,如上例的 Counter 组件:

React.createRefuseRef 都是创建了一个包含current属性的对象,绑定ref时,对应的属性和函数都在current对应的对象中。

查看对应的TypeScript类型,React.createRef创建的是React.RefObject类型,是只读的。

useRef创建的是React.MutableRefObject,是可读写的。可以保存任何可变的值,使用方式类似于class组件的this实例变量。(又是和class组件对标的一个点)

文档描述 useRef 为可以在其.current属性中保存一个可变值的“盒子”。

所以实际上应该是,对函数式组件可设置 ref,且设置的 ref 是一个可变对象,存放组件的变量,也能通过useImperativeHandle访问函数式组件的方法。 但是并不能像将 ref 设置到 class 组件和 DOM 元素上那样获取到对应的实例。

React 函数式组件的 Ref 和子组件访问(useImperativeHandle)的更多相关文章

  1. uni-app 父组件引用子组件时怎么调用子组件的方法

    1.写一个简单的子组件main/index.vue: <template> <view> </view> </template> <script& ...

  2. Vue 父组件主动获取子组件的值,子组件主动获取父组件的值

    父组件主动获取子组件的值 1. 在调用子组件的时候定义一个ref-> ref="header"2. 在父组件中通过this.$refs.header.属性,调用子组件的属性, ...

  3. Vue2.x中的父组件数据传递至子组件

    父组件结构 template <template> <div> <v-girl-group :girls="aGirls"></v-gir ...

  4. vue 高级属性父组件provide向子组件发送数据,子组件通过inject接收数据

    以前父组件向子组件中传值是通过props传值,子组件不能更改父组件中的值,但是可以通过从父组件中获取的值定义给自己的data值,这里父组件可以通过provide向子组件传递自己组件中的data值,子组 ...

  5. vue 父组件传递数据给子组件

    父组件 <body> <div id="app"> <child v-bind:data = "test"></chi ...

  6. vue组件中的样式属性:scoped,解决在父组件中无法修改子组件样式问题

    Scoped CSS规范是Web组件产生不污染其他组件,也不被其他组件污染的CSS规范. vue组件中的style标签标有scoped属性时表明style里的css样式只适用于当前组件元素,它是通过使 ...

  7. Vue : props 使用细节(父组件传递数据给子组件)

    props使用细节 在Vue.js中我们可以使用 props 实现父组件传递数据给子组件,下面我们总结一下props的使用细节 1.基础类型检查 2.必填数据 3.默认值 4.自定义验证函数 其中每一 ...

  8. vue中父组件传数据给子组件

    父组件: <template> <parent> <child :list="list"></child> //在这里绑定list对 ...

  9. vue 父组件数据修改,子组件数据未修改

    页面: 父组件  <myfeedback></myfeedback>  子组件  <news></news> myfeedback.vue <te ...

随机推荐

  1. Flash图解线程池 | 阿里巴巴面试官希望问的线程池到底是什么?

    前言 前几天小强去阿里巴巴面试Java岗,止步于二面. 他和我诉苦自己被虐的多惨多惨,特别是深挖线程和线程池的时候,居然被问到不知道如何作答. 对于他的遭遇,结合他过了一面的那个嘚瑟样,我深表同情(加 ...

  2. JavaScript(二)——在 V8 引擎中书写最优代码

    概述 一个 JavaScript 引擎就是一个程序或者一个解释程序,它运行 JavaScript 代码.一个 JavaScript 引擎可以用标准解释程序或者即时编译器来实现,即时编译器即以某种形式把 ...

  3. Flink-v1.12官方网站翻译-P023-The Broadcast State Pattern

    广播状态模式 在本节中,您将了解如何在实践中使用广播状态.请参考状态流处理,了解状态流处理背后的概念. 提供的API 为了展示所提供的API,我们将在介绍它们的全部功能之前先举一个例子.作为我们的运行 ...

  4. Angular写一个Form组件-TagInput

    前端开发少不了和表单打交道; Angular中, 提供了强大的表单的支持, 响应式表单(Reactive Form) 和 模板驱动的表单(Template-driven Form) 的双向数据流给我们 ...

  5. C# 如何复制(拷贝)Label控件上的文本【新方法】

    Label控件在目前是无法直接调用成员函数来复制其文本内容.其实网络上有很多热心程序员网民解答过这个问题,百度上也可以搜索到,不过大多数人建议使用 TextBox 并把边框调整为不可见(运行时文本框看 ...

  6. AtCoder Beginner Contest 176

    比赛链接:https://atcoder.jp/contests/abc176 A - Takoyaki #include <bits/stdc++.h> using namespace ...

  7. Codeforces Round #622 (Div. 2) C2. Skyscrapers (hard version)(单调栈,递推)

    Codeforces Round #622 (Div. 2) C2. Skyscrapers (hard version) 题意: 你是一名建筑工程师,现给出 n 幢建筑的预计建设高度,你想建成峰状, ...

  8. Codeforces Round #692 (Div. 2, based on Technocup 2021 Elimination Round 3) C. Peaceful Rooks (思维,dsu找环)

    题意:一个棋盘上有一些"车",现在要让这些"车"跑到左倾斜的对角线上,每次可以移动一个棋子,但是棋盘的任意时刻都不能出现一个"车"能吃另一个 ...

  9. python+selenium+bs4爬取百度文库内文字 && selenium 元素可以定位到,但是无法点击问题 && pycharm多行缩进、左移

    先说一下可能用到的一些python知识 一.python中使用的是unicode编码, 而日常文本使用各类编码如:gbk utf-8 等等所以使用python进行文字读写操作时候经常会出现各种错误, ...

  10. P1541 乌龟棋(DP)

    题目背景 小明过生日的时候,爸爸送给他一副乌龟棋当作礼物. 题目描述 乌龟棋的棋盘是一行NNN个格子,每个格子上一个分数(非负整数).棋盘第1格是唯一的起点,第NNN格是终点,游戏要求玩家控制一个乌龟 ...