reactjs - 自定义钩子:不可变的 useState 来存储函数并避免无限循环
问题描述
是否可以使用 useState 挂钩来存储包含函数的不可变对象,以在自定义挂钩中更新另一个状态?
我有一个自定义 Hook,用于简化应用程序中 Timeouts 的使用:
export const useTimer = () => {
const [timer, setTimerObject] = useState<NodeJS.Timeout | null>(null)
//Function to set a new timer. If a timer is already present it is
//canceled and substituted (useEffect)
function setTimer(callback:(...args:any[])=>void,ms:number){
setTimerObject(setTimeout(callback,ms))
}
//Function to clear the timer (if there's one)
function clearTimer(){
setTimerObject(null);
}
//shouldComponentUpdate: delete previous timer if a new timer is issued
//work also as componentWillUnmount
useEffect(() => {
return () => {
if (timer) clearTimeout(timer)
}
}, [timer])
return ({
setTimer,
clearTimer
})
}
因为我在钩子结束时返回一个对象,所以每次我在 useEffect 钩子中设置一个新计时器时,它都会启动一个无限循环。
const timer = useTimer();
useEffect(() => {
if (showError===null) { //Just a random condition to
setShowError("My error");
timer.setTimer(() => setShowError(null), 2000) //INFINITE LOOP
}
}, [timer, showError])
为了避免在每个设置状态下重新渲染,我修改了我的钩子以使用包含更新计时器的函数的固定状态。我没有使用两个 useCallback() 钩子,因为如果我的自定义钩子返回一个包含两个回调的对象,则回调地址保持不变,但指向回调的对象将在每次 setState 调用时发生变化,从而产生无限循环。但是,通过使用不可变状态 Hook,对象引用不会改变,从而允许我的自定义钩子避免无限循环。这种方法是否会导致可能导致我的代码出现意外行为的问题,或者是否存在满足此特定用例的反应挂钩?
export const useTimer = () => {
const [timer, setTimerObject] = useState<NodeJS.Timeout | null>(null);
//HERE THE IMMUTABLE STATE OBJECT
const [functions] = useState({
setTimer : (callback:(...args:any[])=>void,ms:number)=>{
setTimerObject(setTimeout(callback,ms))
},
clearTimer : ()=>{
setTimerObject(null)
}
});
//shouldComponentUpdate: delete previous timer if a new timer is issued
//work also as componentWillUnmount
useEffect(() => {
return () => {
if (timer) clearTimeout(timer)
}
}, [timer])
return functions;
}
解决方案
尝试这样的事情
const useTimer = () => {
const ref = useRef({
setTimer: function setTimer(callback:(...args:any[])=>void,ms:number){
setTimerObject(setTimeout(callback,ms))
},
clearTimer: function clearTimer(){
setTimerObject(null);
}
});
return ref.current;
}
推荐阅读
- c# - SQL 如何替换一个值
- python - 使用 python 和请求,如何进行正确的 POST 调用以及在哪里找到标头?
- android - FirebaseAppDistribution:appDistributionUpload gradle 命令中缺少应用程序 ID
- mysql - 跨列和行计算值的实例
- javascript - 使用 redux 钩子时使用 redux 操作的最佳选择是什么?
- sql - Impala 2.11:AnalysisException:选择列表中不支持子查询
- swift - 在 SwiftUI 中获取当前的经纬度
- c++ - 是否可以禁止一个类被动态转换为?
- git - Git - 将发布分支合并到主分支
- tensorflow - 任何想法如何解决激活函数的问题?