1. useRef介绍
在react开发中,我们通常使用useRef做什么操作?大部分开发者应该都会使用useRef来操作DOM,其实useRef还有一个比较常用的途径,官方给其的定义是:- 用 ref 引用一个值
简单来说,使用useRef来引用一个值,类似于useState,但不同的是,useRef的改变不会触发组件更新
2.何时使用useRef
1.最常用的是操作页面DOM,当我们需要获取页面属性时,可以使用js的方法, document.getElementById('title'),但会使得页面变得更加复杂,更好的方法是绑定useRef,通过useRef拿到DOM属性,例如当前组件的长宽,控制视频播放或暂停,页面整体的缩放等等..
2.当我们在js或jsx中创建一个值时,定义一个变量使用 let ,定义一个常量使用 const,通常在于用户的交互中,useState记录用户的操作并做出反应,可以给用户更好的体验
但在部分程序中,我们希望可以有值保存数据,但是不必要展示给用户,而更改此数据,也不必要重新渲染,此时,useRef的作用就体现出来了,例如:
当我们需要记录用户点击按钮次数,控制台可以依次打印出1,2,3...
import { useRef } from "react";
const Test: React.FC = () => {
const btn = useRef(0);
function hangNext() {
btn.current++;
console.log(btn.current);
}
return <button onClick={hangNext}>下一页</button>;
};
export default Test;
此时,你一定会有疑问,使用let定义一个变量,同样的,当点击按钮时,btn同样可以更新递加,并打印出1,2,3...
const Test: React.FC = () => {
let btn = 0;
function hangNext() {
btn++;
console.log(btn);
}
return <button onClick={hangNext}>下一页</button>;
};
export default Test;
但是,当我们把页面稍微增加点功能,如: 每次点击按钮都会切换page,使组件重新渲染,此时,btn继续递加,但如果使用let btn=0,我们会发现,btn的值永远是1..,因为重新渲染组件后,let也会重新创建,此时useRef的优点将体现出来: 你可以在重新渲染之间 存储信息,
import { useRef, useState } from "react";
const Test: React.FC = () => {
const [page, setPage] = useState(false);
const btn = useRef(0);
function hangNext() {
setPage(!page);
btn.current++;
console.log(btn);
}
return <button onClick={hangNext}>{page ? "下一页" : "上一页"} </button>;
};
export default Test;
综上可以得出:
当我们定义常量时,使用const,例:const city=["上海","北京"]
当我们定义变量,更新不必要渲染页面时,使用useRef,例:const count=useRef(0)
当我们定义变量,更新时需要重新渲染页面,与用户形成交互时,使用useState,例:const [count,setCount]=useState(0)
3.使用方法
使用useRef时,可以把他作为一个变量,如同js中操作一样,可以根据需要定义为string,number,object ,当我们需要发送更改的数据时,可以直接使用value.current,
//创建
const count=useRef(0)
const countObj=useRef({})
const countArr=useRef([])
//使用
count.current=1
countObj.current.name='小明'
countArr.current=[1,2,3]
同样的,当我们使用useRef去操作DOM时,需要使用ref绑定到DOM上,然后拿到其属性
import {useEffect, useRef } from "react";
const Test: React.FC = () => {
const divRef = useRef(null);
console.log(1, divRef); // 1 {current: null} ,在组件渲染阶段,DOM还未创
useEffect(() => {
console.log(2, divRef); // 2 {current: div} ,绑定成功,可以拿到DOM
}, []);
return <div ref={divRef}></div>;
};
export default Test;
4.进阶-useImperativeHandle
在React组件中,我们通常会在子组件中,调用父组件的变量和方法,但在某些情况下,我们需要在父组件中,使用子组件的变量和方法,此时应当如何操作?
在React的官方解释中,推荐使用Props解决问题,仅当无法使用Props解决问题时,可以考虑使用useRef 例:
const Test = () => {
return (
<>
<Card />
<button>点击按钮</button>
</>
);
};
export default Test;
const Card = () => {
function handCard() {
alert("点击了按钮");
}
return <button onClick={handCard}>按钮</button>;
};
当我们需求输入框填入数据后,点击父组件的button,希望调用子组件的handCard方法时,可以使用useRef,暴露出子组件的方法:
import { useRef, useImperativeHandle, forwardRef } from "react";
const Test = () => {
const cardRef = useRef(null);
return (
<>
<Card ref={cardRef} />
<button onClick={() => cardRef.current?.handCard()>点击按钮</button>
</>
);
};
export default Test;
const Card = forwardRef((props, ref) => {
function handCard() {
alert("点击了按钮");
}
useImperativeHandle(
ref, () => { return { handCard }
},[]);
return <button onClick={handCard}>按钮</button>;
});
5.总结
useRef需要注意点有两个:
1.定义更新不需要渲染到页面的变量(区别常量)
2.操作DOM时,注意组件渲染周期(页面挂载完成后,才能使用)
纸上学来终觉浅,欲知此事要躬行!