React Hook
上篇文章中我们介绍了React中最常用也是最重要的两个Hook,useState以及useEffect,本次的文章我们将继续介绍React中常用的另外一个Hook——useRef。同时将useRef与useState进行对比,希望可以帮助大家更好地理解并使用钩子函数。
useRef - 持久化引用
函数定义
useRef是一个较为常用的钩子函数,用于在函数组件中创建一个可以存储和访问持久性数据的引用。它主要用于在组件中存储和获取DOM节点、保存上一个状态值等持久性数据。
useRef 的用法和特点:
- 创建引用: 你可以使用
useRef创建一个引用,并将其初始化为null或其他初始值。 - 持久性数据存储:
useRef创建的引用可以在组件的整个生命周期中保持不变。每次组件重新渲染时,useRef返回的引用都是相同的。 - 不触发重新渲染: 与状态钩子不同,修改
useRef创建的引用并不会触发组件的重新渲染。 - 访问 DOM 节点:
useRef最常见的用途之一是获取DOM元素的引用,用于执行DOM操作。
使用示例
下面是一个示例,展示了如何使用useRef来获取DOM节点的引用:
import React, { useRef, useEffect } from 'react';
function FocusInput() {
const inputRef = useRef(null);
useEffect(() => {
// 在组件挂载后,将输入框自动聚焦
inputRef.current.focus();
}, []);
return (
<div>
<input ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>聚焦输入框</button>
</div>
);
}
function App() {
return (
<div>
<h1>使用 useRef 示例</h1>
<FocusInput />
</div>
);
}
export default App;
在这个示例中,inputRef 是一个引用,使用 useRef(null) 初始化。在 useEffect 中,当组件挂载后,inputRef.current.focus() 会自动聚焦输入框。同时,通过点击按钮也可以再次聚焦输入框。
此外你也可以通过ref.current.style.xxx来修改DOM节点的样式:
import React, {useRef} from 'react';
function App() {
const divRef = useRef(null);
const changeColor = () => {
divRef.current.style.backgroundColor = divRef.current.style.backgroundColor === 'lightblue'?'white':'lightblue'
};
return (
<div>
<button onClick={changeColor}>改变背景颜色</button>
<div
ref={divRef}
style={{
width: '200px',
height: '200px',
}}
></div>
</div>
);
}
export default App;
useRef与useState的区别
上篇文章中我们谈到了useState钩子函数,和本次的useRef一样,两者都可用于在函数组件中声明和管理数据,但它们的实际用途和作用却有些不同。下面是它们之间的主要区别:
-
触发重新渲染:
useState:当使用useState来声明状态并通过对应的更新函数修改状态时,React 会自动识别状态变化,并重新渲染组件,以反映状态的变化。useRef:useRef创建的引用变化不会触发组件的重新渲染,因为它主要用于存储数据和获取 DOM 节点引用,而不是用于触发渲染。
-
数据持久性:
useState:状态变量是可变的,会随着组件的重新渲染而重新初始化,不具备持久性。useRef:引用变量是持久的,不会随着组件的重新渲染而重新初始化,可以用来存储需要在多次渲染之间保持一致的数据。
综上所述,useState 主要用于状态管理和触发组件的重新渲染,而useRef主要用于获取持久性的引用和操作 DOM,不触发组件的重新渲染。
在绝大多数情况下,推荐使用 useState 来管理组件的状态,这样可以更好地利用React的状态管理机制,保持代码的可维护性和可预测性。如果需要直接操作DOM,可以在特定情况下使用 useRef,但要小心绕过了React的控制,可能会导致代码难以追踪和维护。
渲染机制
上面讲到了useState与useRef函数一个很大的区别就在于是否会触发组件的重新渲染。但如果只是修改DOM节点的样式,我们并不能分辨出两者的区别。下面我们将通过使用useEffect钩子函数来感受两者的区别。
使用useState:
import React, {useEffect, useState} from 'react';
function App() {
// 组件初始化的过程中会执行一次useEffect
// 在组件每次渲染完成后也会执行一次
useEffect(() => {
console.log('Component re-rendered');
});
const [isBlue, setIsBlue] = useState(true);
const toggleColor = () => {
setIsBlue(!isBlue);
};
return (
<div>
<button onClick={toggleColor}>切换颜色</button>
<div
style={{
width: '200px',
height: '200px',
backgroundColor: isBlue ? 'lightblue' : 'white'
}}
></div>
</div>
);
}
export default App;
浏览器响应效果及控制台展示:
使用useRef:
import React, {useEffect, useRef} from 'react';
// App 组件
function App() {
// 组件初始化的过程中会执行一次useEffect
// 在组件每次渲染完成后也会执行一次
useEffect(() => {
console.log('Component re-rendered');
});
const divRef = useRef(null);
const changeColor = () => {
divRef.current.style.backgroundColor = divRef.current.style.backgroundColor === 'lightblue'?'white':'lightblue'
};
return (
<div>
<button onClick={changeColor}>改变背景颜色</button>
<div
ref={divRef}
style={{
width: '200px',
height: '200px',
backgroundColor: 'lightblue',
}}
></div>
</div>
);
}
export default App;
浏览器响应效果及控制台展示:
通过对比可以看出,使用useRef钩子函数对DOM样式进行修改不会触发组件的重新渲染,而每一次调用useState修改状态都会触发组件的重新渲染。
总结
通过本次的学习,相信大家对useRef钩子函数的定义与使用都有了一定的了解。此外,通过对比我们也可以更加明确不同钩子函数的使用场景。由于过多地使用 useState 可能会导致频繁的组件重新渲染,从而影响性能。所以在遇到需要操作DOM的业务场景时,我们经常可以使用useRef快速解决问题。同时我们也需要利用useRef的特性帮助我们存储持久性数据以及维护引用的稳定性。
[第一篇](盘点React常用Hooks(1)—— useState、useEffect | 青训营 - 掘金 (juejin.cn))