海星吧的react学习:useRef、forwardRef和useImperativeHandle。

海星吧 2023-3-3 5163

乘着睡觉前再来一篇关于函数式组件的 Ref 使用。

学过vue2的人都知道,this.$refs和组件里的 ref属性,

还有vue3的useRef绑定Dom。

<template>
	<div>
		<input type="text" ref="inputRef" />
		<button @click="lookInputRef">click me</button>
	</div>
</template>
<script>
export default {
	methods: {
		lookInputRef() {
			console.log(this.$refs.inputRef);
		},
	},
};
</script>
<template>
	<div>
		<input type="text" :ref="inputRef" />
		<button @click="lookInputRef">click me</button>
	</div>
</template>
<script setup>
import {useRef} from "vue"
const inputRef = useRef(null)
function lookInputRef() {
    console.log(inputRef.value);
}
</script>

以上就是vue关于绑定Dom元素的写法,那么react是怎么写的呢?

import React, { useRef, useImperativeHandle, forwardRef, createRef } from "react";

function TestUseRef() {
	/*
		useRef() 返回一个获取dom的值,把这个值放到需要获取的dom上,用ref绑定。
		const inputRef = useRef()
		<input type="text" ref={inputRef}/>
		这样以来 inputRef 就会被赋予一个 current 值,这个值就是 input 的实例对象
	*/
	const inputRef = useRef<HTMLInputElement | null>(null)
	const lookInputRef = () => {
		console.log(inputRef.current);
	}
	return (
		<div>
			<input type="text" ref={inputRef}/>
			<button onClick={lookInputRef}>
				看看值
			</button>
		</div>
	)
}
export default TestUseRef;

点击按钮之后就打印了这个实例对象。

那么要获取子组件的dom是怎么样呢。

function TestUseRef() {
	/*
		useRef() 返回一个获取dom的值,把这个值放到需要获取的dom上,用ref绑定。
		const inputRef = useRef()
		<input type="text" ref={inputRef}/>
		这样以来 inputRef 就会被赋予一个 current 值,这个值就是 input 的实例对象
	*/
	const inputRef = useRef<HTMLInputElement | null>(null);
	const inputRefFoo = useRef<HTMLInputElement | null>(null);
	const lookInputRef = () => {
		console.log(inputRef.current);
		console.log(inputRefFoo.current);
	};
	return (
		<div>
			<input type="text" ref={inputRef} />
			<button onClick={lookInputRef}>看看值</button>
			<Child ref={inputRefFoo}></Child>
		</div>
	);
}

const Child = forwardRef((props: any, inputRef: any) => {
	// 这里被forwardRef包裹后 第二个参数 是父组件传入的 ref,所以可以不用创建ref了
	// const inputRef = useRef<HTMLInputElement | null>(null);
	const handleInputFocus = () => {
		console.log(inputRef);
		inputRef.current?.focus();
	};
	return (
		<div>
			<input type="text" ref={inputRef} placeholder="input me!" />
			<button onClick={handleInputFocus}>click to do input focus</button>
		</div>
	);
});

export default TestUseRef;

用forwardRef把子组件包裹起来,这样子组件的第二个参数就变成了一个ref值,

代表父组件通过ref属性传递过来的 inputRefFoo

那如果我只想让父组件获得 handleInputFocus 这个函数而不是这个 input实例对象呢。

可以用 useImperativeHandle 将内部函数返回出去。

const Child = forwardRef((props: any,  Ref: any) => {
	//需要使用 useImperativeHandle 将函数抛出, 父组件才能使用 ref 中的方法
	useImperativeHandle(Ref, () => ({
		handleInputFocus: () => handleInputFocus,
	}));
	const inputRef = useRef<HTMLInputElement | null>(null);
	const handleInputFocus = () => {
		console.log(inputRef);
		// inputRef.current?.focus();
	};
	return (
		<div>
			<input type="text" ref={inputRef} placeholder="input me!" />
			<button onClick={handleInputFocus}>click to do input focus</button>
		</div>
	);
});

inputRef在子组件内部自己创建,然后用useImperativeHandle将函数 handleInputFocus 返回出去,

这样就可以让父组件使用子组件内部的方法了。

 

以上总结一下,

useRef 创建一个获取dom的对象,这个对象被赋予了一个属性为 current 的值,这个值就是绑定的Dom实例。

父组件要使用子组件中被创建的dom实例就得用 forwardRef 方法把子组件包起来,这个子组件的第二个参数就变成了父组件

传入的ref实例,这样父组件就能在自己的函数里使用子组件的Dom实例了。

当然子组件也可以自己管理自己的实例,用 useImperativeHandle 把父组件传入的Ref放进第一个参数里,

然后第二个参数里吧自身的函数返回出去,这样父组件就只获得子组件的方法而不是实例了。

弱鸡程序员年底还在加班
最新回复 (3)
  • 联盟X 2023-3-3
    0 2
    看不懂,受震撼
    匡扶汉室!
  • 良稗君 2023-3-3
    0 3
    不明觉厉
    ₍₍(ง`ᝫ´ )ว⁾
  • 猪肝饭 2023-3-5
    0 4
    紧急避孕
    好好学习,天天向上。
    • ACG里世界
      5
          
返回
发新帖