这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战
当你希望组件“记住”一些信息,但是你不希望这些信息触发重新 render 的时候,你可以使用 ref,它像一个秘密的“口袋”,用于在组件中存储信息。
后面的文章主要是通过一些例子检验对 ref 的理解,实际在 react 官方博客上可以执行调试,文章主要是分析,可以对照着看:beta.reactjs.org/learn/refer…
系列文章
- useRef 简单易懂解析(一)useRef 的使用
- useRef 简单易懂解析(二)useRef 的例子
- useRef 简单易懂解析(三)ref 和 state 的对比
- useRef 简单易懂解析(四)useRef 的原理
- useRef 简单易懂解析(五)ref 的最佳实践
- useRef 简单易懂解析(六)ref 的总结
- useRef 简单易懂解析(七)你学会了吗,来试试这些挑战 - 修复坏掉的聊天输入框
- useRef 简单易懂解析(八)你学会了吗,来试试这些挑战 - 修复无法重新渲染的组件
修复去抖动
挑战 3(共 4 个):修复去抖动 在这个例子中,所有按钮点击处理程序都被“去抖动”了。如果我按下其中一个按钮,一秒钟后会弹出提示消息。如果你在等待消息时按下按钮,计时器将重置。因此,如果你多次快速单击同一个按钮,则直到你停止单击后,过一秒钟才会显示该消息。去抖动可以让你延迟一些动作,直到用户“停止做事”。
这个例子是有效的,但并不完全符合预期。因为这些按钮不是独立的。如果单击其中一个按钮,然后立即单击另一个按钮。预期是在延迟之后,将看到两个按钮的消息。但是现在的状况是,只显示最后一个按钮的消息。第一个按钮的消息丢失了。
那么为什么这些按钮会相互干扰?找到并修复这个问题。
import { useState } from 'react';
let timeoutID;
function DebouncedButton({ onClick, children }) {
return (
<button onClick={() => {
clearTimeout(timeoutID);
timeoutID = setTimeout(() => {
onClick();
}, 1000);
}}>
{children}
</button>
);
}
export default function Dashboard() {
return (
<>
<DebouncedButton
onClick={() => alert('Spaceship launched!')}
>
Launch the spaceship
</DebouncedButton>
<DebouncedButton
onClick={() => alert('Soup boiled!')}
>
Boil the soup
</DebouncedButton>
<DebouncedButton
onClick={() => alert('Lullaby sung!')}
>
Sing a lullaby
</DebouncedButton>
</>
)
}
3
2
1
因为 timeoutID 变量在所有组件之间共享。所以单击第二个按钮会重置第一个按钮的 timeout。为了解决这个问题,你可以将 timeout 存在 ref 中。每个按钮都有自己的 ref,所以它们不会相互冲突。这样,快速单击两个按钮就将会显示两个消息。
import { useState, useRef } from 'react';
function DebouncedButton({ onClick, children }) {
const timeoutRef = useRef(null); // 这里
return (
<button onClick={() => {
clearTimeout(timeoutRef.current);
timeoutRef.current = setTimeout(() => {
onClick();
}, 1000);
}}>
{children}
</button>
);
}
export default function Dashboard() {
return (
<>
<DebouncedButton
onClick={() => alert('Spaceship launched!')}
>
Launch the spaceship
</DebouncedButton>
<DebouncedButton
onClick={() => alert('Soup boiled!')}
>
Boil the soup
</DebouncedButton>
<DebouncedButton
onClick={() => alert('Lullaby sung!')}
>
Sing a lullaby
</DebouncedButton>
</>
)
}
注意这里 ref 是在 DebouncedButton 组件中初始化的,这个例子来说没有问题,是因为组件不会重新进行 render(修改 ref 不会重新触发 render),如果会组件会 render 多次的话,需要把 ref 放到外面定义。