useRef
useRef
允许创建一个对元素或值的可变引用,创建的初始值为传入的参数,它的返回值是一个ref对象,这个对象使用.current
属性就是该数据的最新值。它通常用于直接访问和管理DOM元素,存储持久值,或处理不应触发重新渲染的值。
- 返回一个可变的 ref 对象,该对象只有个
current
属性,初始值为传入的参数 - 返回的 ref 对象在组件的整个生命周期内保持不变,
useRef
类似于类组件的this
- 当更新
useRef
创建的值时并不会触发组件的重新渲染,这是与useState
不同的地方 - 更新
useRef
创建的值是副作用操作,所以一般写在useEffect
或事件处理函数里
语法和使用
const refContainer = useRef(initialValue);
多个同名ref情况
当页面中多个相同的同名的ref,后者会覆盖前者,只会获取一个。代码示例如下:
import {useRef} from 'react'
export default function App() {
let test=useRef();
let look=()=>{
console.log(test.current);
}
return (
<div>
<p ref={test}>App组件</p>
<p ref={test}>test</p>
<button onClick={look}>look</button>
</div>
)
}
访问DOM元素
使用useRef
的一个最简单的情况就是在函数组件里面获取DOM对象的引用。可以使用useRef
直接引用组件内的元素,而不是使用document.getElementById
或其他DOM的API来查询。
案例:组件挂载后输入框自动聚焦
import {useRef,useEffect} from 'react'
export default function App() {
// 使用useRef直接引用组件内的元素
let inputel=useRef();
useEffect(()=>{
console.log(inputel);
// 聚焦输入元素
inputel.current.focus();
})
return (
<div>
<p>App组件</p>
<input type="text" ref={inputel}/>
</div>
)
}
通过
useRef
定义inputel
变量,在input
元素上定义ref={inputel}
,这样通过inputel.current
就可以获取到input
Dom 元素,然后调用下focus
函数进行获取焦点。
存储之前的值
使用useRef
存储一个跨渲染持久的值,但更新它不会触发重新渲染。一个组件的state
可以在多次渲染之后依旧不变。但是state
的问题在于一旦修改了它就会造成组件的重新渲染。如果需要保存一些数据,并希望这些数据在组件重新渲染时不会丢失,可以使用 useRef
。
import { useEffect, useRef, useState } from 'react';
export default function App() {
const count = useRef(0);
const [msg, setMsg] = useState([])
useEffect(() => {
count.current = count.current + 1;
});
const changeHandle = () => {
const arr = [...msg];
arr.push(7)
setMsg(arr);
};
return (
<>
<div>
页面加载了 {count.current} 次。
</div>
<button onClick={changeHandle}>修改msg</button>
</>
)
}
获取组件(结合forwardRef)
useRef
可以直接获取类组件,不能使用这个直接获取函数组件,会得到undefined
,需要配合forwardRef
使用。
forwardRef
是一个高阶函数(一个函数就可以接收另一个函数作为参数就是高阶函数),用于将 ref 显式地转发到组件的子组件中。这对于高阶组件(高阶组件是一个函数,它接受一个组件作为参数,并返回一个新的组件)特别有用,因为它允许父组件通过 ref 访问子组件的实例或 DOM 节点。
import { Component, useRef, forwardRef } from 'react'
class Home extends Component {
render() {
return (
<div>Home</div>
)
}
}
function Son() {
return (
<div>Son</div>
)
}
const MyInput = forwardRef((props, ref) => (
<input ref={ref} {...props} />
));
export default function App() {
let classTest = useRef();
const funTest = useRef();
const inputRef = useRef();
let look = () => {
// 获取类组件
console.log(classTest.current, "classTest");
// 获取函数组件
console.log(funTest.current, "funTest");
console.log(inputRef.current, "inputRef")
};
const focusInput = () => {
// 父组件通过 ref 访问子组件的实例或 DOM 节点,这里父组件访问了MyInput子组件的input这个DOM元素,并执行了聚焦的focus方法
inputRef.current.focus();
};
return (
<div>
<p>App组件</p>
<Home ref={classTest}></Home>
<button onClick={look}>look</button>
<br />
<MyInput ref={inputRef} />
<button onClick={focusInput}>聚焦输入框</button>
<Son ref={funTest}></Son>
</div>
)
}
useImperativeHandle和useRef
useImperativeHandle
用于自定义暴露给父组件的 ref 行为。它允许在子组件中定义哪些方法或属性可以通过 ref 被父组件访问。
import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react';
const MyInput = forwardRef((props, ref) => {
const [value, setValue] = useState('');
useImperativeHandle(ref, () => ({
// 以下定义的方法父组件可以通过ref进行访问
focus: () => {
inputRef.current.focus();
},
getValue: () => value,
setValue: (newValue) => setValue(newValue)
}));
const inputRef = useRef(null);
return (
<input ref={inputRef} value={value} onChange={(e) => setValue(e.target.value)} {...props} />
);
});
function App() {
const inputRef = useRef();
const focusInput = () => {
inputRef.current.focus();
};
const getInputValue = () => {
alert(inputRef.current.getValue());
};
const setInputValue = () => {
inputRef.current.setValue('Hello, World!');
};
return (
<>
<MyInput ref={inputRef} />
<button onClick={focusInput}>聚焦输入框</button>
<button onClick={getInputValue}>获取输入框值</button>
<button onClick={setInputValue}>设置输入框值</button>
</>
);
}
export default App;