理解hooks原理 --- useRef
前言
react hooks 是 React 16.8 的新增特性。 它可以让我们在函数组件中使用 state 、生命周期以及其他
react 特性,而不仅限于 class 组件。react hooks 的出现,标示着
react 中不会在存在无状态组件了,只有类组件和函数组件。具体可查看官网。
优势
函数组件不能使用 state,遇到交互更改状态等复杂逻辑时不能更好地支持,hooks 让函数组件更靠近 class 组件,拥抱函数式编程。
解决副作⽤问题,hooks 出现可以处理数据获取、订阅、定时执行任务、手动修改
ReactDOM 这些⾏为副作用,进行副作用逻辑。比如 useEffect。 更好写出有状态的逻辑重用组件。
让复杂逻辑简单化,比如状态管理:useReducer、useContext。
函数式组件比 class 组件简洁,开发的体验更好,效率更⾼,性能更好。 更容易发现无用的状态和函数。
useRef 介绍
/**
useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref
对象在组件的整个生命周期内保持不变。
本质上,useRef 就像是可以在其 .current 属性中保存一个可变值的“盒子”。
然而,useRef() 比 ref 属性更有用。它可以很方便地保存任何可变值,其类似于在 class 中使用实例字段的方式。
这是因为它创建的是一个普通 Javascript 对象。而 useRef() 和自建一个 {current: …}
对象的唯一区别是,useRef 会在每次渲染时返回同一个 ref 对象。
请记住,当 ref 对象内容发生变化时,useRef 并不会通知你。变更 .current 属性不会引发组件重新渲染。如果想要在 React
绑定或解绑 DOM 节点的 ref 时运行某些代码,则需要使用回调 ref 来实现。
*/
import React, { useRef } from "react";
const refContainer = useRef(initialValue);
javascript
useRef 有什么作用呢?
作用于 Dom 元素 获取子组件的实例(只有类组件可用) 在函数组件中的一个全局变量,不会因为重复 render 重复申明, 类似于类组件的
this.xxx useRef 使用 作用于 Dom 元素
const UseRefComp=()=>{
//创建ref
const inputRef=useRef()
const getValue= () => {
//访问ref
console.log(inputRef.current.value) }
//挂载
return (
<div>
<input ref={inputRef} type="text"> <button onClick={getValue}>获取input的
值</button> </div>
) }
javascript
获取子组件的实例(只有类组件可用)
// 使用 ref 子组件必须是类组件
class Children extends PureComponent {
render() {
const { count } = this.props;
return <div>{count}</div>;
}
}
function App() {
const [count, setCount] = useState(0);
const childrenRef = useRef(null);
// const
const onClick = useMemo(() => {
return () => {
console.log("button click");
console.log(childrenRef.current);
setCount((count) => count + 1);
};
}, []);
return (
<div>
点击次数: {count}
<Children ref={childrenRef} count={count}></Children>
<button onClick={onClick}>点我</button>
</div>
);
}
javascript
在函数组件中的一个全局变量,不会因为重复 render 重复申明, 类似于类组件的 this.xxx
简单例子:
function Test() {
const t = useRef(null);
const handleClick = () => {
t.current = setTimeout(() => {
console.log("timer");
}, 1000);
};
const handleClear = () => clearTimeout(t.current);
return (
<>
<button onClick={handleClick}>start</button>
<button onClick={handleClear}>clear</button>
</>
);
}
javascript
有些情况下,我们需要保证函数组件每次 render 之后,某些变量不会被重复申明,比如说 Dom 节点,定时器的 id 等等,在类组件中, 我们完全可以通过给类添加一个自定义属性来保留,比如说 this.xxx, 但是函数组件没有 this,自然无法通过这种方法使用,有的朋友说,我可以用 useState 来保留变量的值, 但是 useState 会触发组件 render,在这里完全是不需要的,我们就需要使用 useRef 来实现了,具体看下面例子
function App() {
const [count, setCount] = useState(0);
const timer = useRef(null);
let timer2;
useEffect(() => {
let id = setInterval(() => {
setCount((count) => count + 1);
}, 500);
timer.current = id;
timer2 = id;
return () => {
clearInterval(timer.current);
};
}, []);
const onClickRef = useCallback(() => {
clearInterval(timer.current);
}, []);
const onClick = useCallback(() => {
clearInterval(timer2);
}, []);
return (
<div>
点击次数: {count}
<button onClick={onClick}>普通</button>
<button onClick={onClickRef}>useRef</button>
</div>
);
}
javascript
当我们们使用普通的按钮去暂停定时器时发现定时器无法清除,因为 App 组件每次 render,都会重新申明一次 timer2, 定时器的 id 在第二次 render 时,就丢失了,所以无法清除定时器,针对这种情况, 就需要使用到 useRef,来为我们保留定时器 id,类似于 this.xxx,这就是 useRef 的另外一种用法。