如果您使用useRef
存储状态创建简单的计数器应用程序:
import { useRef } from "react";
const App = () => {
const count = useRef(0);
return (
<div>
<h2>count: {count.current}</h2>
<button
onClick={() => {
count.current = count.current + 1;
console.log(count.current);
}}
>
increase count
</button>
</div>
);
};
如果您点击按钮,<h2>count: {count.current}</h2>
这个值不会改变,因为组件没有重新渲染。如果您检查控制台 console.log(count.current)
,您会发现该值实际上在增加,但由于组件没有重新渲染,UI 不会得到更新。
如果您使用 useState
设置状态,则单击按钮将重新渲染组件,因此 UI 将得到更新。
重新渲染是一项昂贵的操作。在某些情况下,您不希望保持应用程序的重新渲染。例如,当您将输入值存储在状态中以创建受控组件时。在这种情况下,对于每个按键,您都会重新渲染应用程序。如果您使用 ref
获取对 DOM 元素的引用,则使用 useState
只会重新渲染组件一次:
import { useState, useRef } from "react";
const App = () => {
const [value, setValue] = useState("");
const valueRef = useRef();
const handleClick = () => {
console.log(valueRef);
setValue(valueRef.current.value);
};
return (
<div>
<h4>Input Value: {value}</h4>
<input ref={valueRef} />
<button onClick={handleClick}>click</button>
</div>
);
};
为了创建简单的翻转动画,我们需要两个状态值。一个是布尔值来在间隔中翻转或不翻转,另一个是在离开组件时清除订阅:
const [isFlipping, setIsFlipping] = useState(false);
let flipInterval = useRef<ReturnType<typeof setInterval>>();
useEffect(() => {
startAnimation();
return () => flipInterval.current && clearInterval(flipInterval.current);
}, []);
const startAnimation = () => {
flipInterval.current = setInterval(() => {
setIsFlipping((prevFlipping) => !prevFlipping);
}, 10000);
};
setInterval
返回一个 id,我们将其传递给 clearInterval
以在离开组件时结束订阅。 flipInterval.current
可能为 null 或该 id。如果我们没有在这里使用 ref
,每次从 null 切换到 id 或从 id 切换到 null,此组件都会重新渲染,从而创建一个无限循环。
- 如果您不需要更新 UI,请使用
useRef
存储状态变量。
假设在 React Native 应用程序中,我们为某些没有对 UI 产生影响的操作设置声音。对于一个状态变量来说,可能没有太多的性能节省,但是如果您玩游戏并且需要根据游戏状态设置不同的声音,则情况就不同了。
const popSoundRef = useRef<Audio.Sound | null>(null);
const pop2SoundRef = useRef<Audio.Sound | null>(null);
const winSoundRef = useRef<Audio.Sound | null>(null);
const lossSoundRef = useRef<Audio.Sound | null>(null);
const drawSoundRef = useRef<Audio.Sound | null>(null);
如果我使用useState
,每次更改状态值时都会重新渲染。
如果您不希望id在每次重新渲染时更改
const id = useRef(uuid.v4());