🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] ## 概述 当你希望组件“记住”某些信息,但又不想让这些信息 触发新的渲染 时,你可以使用 ref 。 由于当数据变动时, 组件会进行刷新如果需要在刷新当前组件时,保留某些变量,可以使用ref ## 区别 | ref | state | | --- | --- | | `useRef(initialValue)`返回`{ current: initialValue }` | `useState(initialValue)`返回 state 变量的当前值和一个 state 设置函数 (`[value, setValue]`) | | 更改时不会触发重新渲染 | 更改时触发重新渲染。 | | 可变 —— 你可以在渲染过程之外修改和更新`current`的值。 | “不可变” —— 你必须使用 state 设置函数来修改 state 变量,从而排队重新渲染。 | | 你不应在渲染期间读取(或写入)`current`值。 | 你可以随时读取 state。但是,每次渲染都有自己不变的 state[快照](https://zh-hans.react.dev/learn/state-as-a-snapshot)。 | ## 场景 通常,当你的组件需要“跳出” React 并与外部 API 通信时,你会用到 ref —— 通常是不会影响组件外观的浏览器 API。以下是这些罕见情况中的几个: * 存储[timeout ID](https://developer.mozilla.org/docs/Web/API/setTimeout) * 存储和操作[DOM 元素](https://developer.mozilla.org/docs/Web/API/Element), * 存储不需要被用来计算 JSX 的其他对象。 如果你的组件需要存储一些值,但不影响渲染逻辑,请选择 ref。 ## 示例 bad ``` import { useState, useRef } from 'react'; export default function Stopwatch() { const [startTime, setStartTime] = useState<number | null>(null); const [now, setNow] = useState<number | null>(null); let interval: NodeJS.Timeout | null = null; // 无法保留 值 console.log("刷新") // 控制台会一直打印 "刷新",因为数据变量导致了刷新组件 function handleStart() { setStartTime(Date.now()); setNow(Date.now()); clearInterval(interval); interval = setInterval(() => { setNow(Date.now()); }, 10); console.log(interval) } function handleStop() { clearInterval(interval); // 当需要停止时, intervalRef 值为null,因为组件已经刷新过 } let secondsPassed = 0; if (startTime != null && now != null) { secondsPassed = (now - startTime) / 1000; } return ( <> <h1>时间过去了: {secondsPassed.toFixed(3)}</h1> <button onClick={handleStart}> 开始 </button> <button onClick={handleStop}> 停止 </button> </> ); } ``` good ``` import { useState, useRef } from 'react'; export default function Stopwatch() { const [startTime, setStartTime] = useState<number | null>(null); const [now, setNow] = useState<number | null>(null); const intervalRef = useRef<NodeJS.Timeout | null>(null); function handleStart() { setStartTime(Date.now()); setNow(Date.now()); clearInterval(intervalRef.current!); intervalRef.current = setInterval(() => { setNow(Date.now()); }, 10); } function handleStop() { clearInterval(intervalRef.current!); } let secondsPassed = 0; if (startTime != null && now != null) { secondsPassed = (now - startTime) / 1000; } return ( <> <h1>时间过去了: {secondsPassed.toFixed(3)}</h1> <button onClick={handleStart}> 开始 </button> <button onClick={handleStop}> 停止 </button> </> ); } ```