useRef
返回一个可变的ref
对象,其.current
属性被初始化为传入的参数(initialValue
)。返回的ref
对象在组件的整个生命周期内保持不变。
系列目录
- React新特性-Context(一)
- React新特性-Lazy和Suspense(二)
- React新特性-memo(三)
- React新特性-Hooks之useState(四)
- React新特性-Hooks之useEffect和useLayoutEffect(五)
- React新特性-Hooks之useContext(六)
- React新特性-Hooks之useRef(七)
- React新特性-Hooks之useMemo和useCallback(八)
- React新特性-Hooks之useReducer(九)
- React新特性-Hooks之自定义Hook(十完结)
useRef的功能
说起ref你可能会想到class 中的获取真实DOM节点的ref。没错,useRef有两种功能。
- 获取子组件或者DOM节点的句柄
- 渲染周期之间共享数据的存储(类似class组件中实例属性)
获取子组件或者DOM节点的句柄
import React, { useState, useEffect, useRef } from 'react'
const TestInput = (props) => {
const [inputValue, setInputValue] = useState('')
const inputEl = useRef(null);
useEffect(() => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
})
return (
<div>
<input ref={inputEl} value={inputValue} onChange={(e) => setInputValue(e.target.value)} /> {inputValue}
<p>{props.visible}</p>
</div>
)
}
export default TestInput
创建个变量inputEl
,页面渲染时将input
的真实DOM
存储到inputEl.current
中,页面渲染完后执行副作用useEffect
,利用真实DOM
让input
输入框获取焦点。
渲染周期之间共享数据的存储
useRef()
Hook 不仅可以用于DOM refs
,ref
对象是一个 current
属性可变且可以容纳任意值的通用容器,类似于一个 class 的实例属性。
你可以在 useEffect
内部对其进行写入:
function Timer() {
const intervalRef = useRef();
useEffect(() => {
const id = setInterval(() => {
// ...
});
intervalRef.current = id;
return () => {
clearInterval(intervalRef.current);
};
});
// ...
}
把定时器的ID存入到useRef
中,这样这个定时器ID不仅仅在useEffect
可以拿到,而且可以在整个组件函数中都可以获取到。
利用useRef获取上一轮的 props 或 state
function Counter() {
const [count, setCount] = useState(0);
const prevCountRef = useRef();
useEffect(() => {
prevCountRef.current = count;
});
const prevCount = prevCountRef.current;
return (
<>
<h1>Now: {count}, before: {prevCount}</h1>
<button onClick={()=>setCount((count)=>count+1)}>增加</button>
</>
);
}
利用页面先渲染先拿到count
值为0,然后此时prevCount
刚刚创建还是个undefined
,当页面渲染完成进入副作用useEffect
中,进行赋值操作prevCountRef.current = count
。这时候count
的值就保存到current
中,但是页面不会重新渲染。
点击按钮增加时页面重新渲染,count
为2,然后prevCount
还存储这上次的1值,只有等进入useEffect
中重新赋值为才能改变。
上面的代码有一点错综复杂,我们可以把它抽取成一个自定义 Hook
function Counter() {
const [count, setCount] = useState(0);
const prevCount = usePrevious(count);
return (
<>
<h1>Now: {count}, before: {prevCount}</h1>
<button onClick={()=>setCount((count)=>count+1)}>增加</button>
</>
);
}
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
这里我们创建了个自定义的Hook叫做usePrevious
,来获取上一次的state。
避免重新创建 useRef() 的初始值
function Image(props) {
// ⚠️ IntersectionObserver 在每次渲染都会被创建
const ref = useRef(new IntersectionObserver(onIntersect));
// ...
}
useRef
不会像useState
那样接受一个特殊的函数重载。相反,你可以编写你自己的函数来创建并将其设为惰性的
function Image(props) {
const ref = useRef(null);
// ✅ IntersectionObserver 只会被惰性创建一次
function getObserver() {
if (ref.current === null) {
ref.current = new IntersectionObserver(onIntersect);
}
return ref.current;
}
// 当你需要时,调用 getObserver()
// ...
}
这避免了在一个对象被首次真正需要之前就创建它。