useSafeState-保障 React 状态的安全性

useSafeState-保障 React 状态的安全性

摘要:ahooks 是一个可靠的 React Hooks 库。本文将详细介绍 useSafeState 这个 Hook,帮助您理解其工作原理和应用场景。官方地址:ahooks useSafeState

简介

useSafeState 是一个用于管理可安全更新的状态的 Hook。相较于 useStateuseSafeState 提供了以下两个主要改进:

  1. 安全性 :当组件被卸载时,useSafeState 会停止状态更新,避免因状态更新导致的错误。

  2. 可选的初始状态useSafeState 允许提供一个初始状态,或者提供一个函数来生成初始状态。

相信大家也见到过如下图所示的警告:

以上问题意思就是说React组件在卸载的时候还在更新状态,这可能会导致内存泄漏的风险,而修复的方式就是在useEffect钩子函数中取消所有的监听以及异步任务,即停止更新状态。

useSafeState实现原理

useSafeState就是为了处理掉这个问题的。如果没有这个hook,我们可以思考一下这个hook的实现原理。分为2步:

  1. 如何判断组件是否卸载。

  2. 如何阻止状态的更新。

针对这2个问题,我们就来探讨一下这个hook的实现原理,其实这2个问题也可以分别封装成2个hook。

如何判断组件是否卸载

首先我们来看解决第1个问题的hook,我们可以创建一个不需要渲染的状态,这里我们只是需要用这个状态来做判断,不需要渲染,因此就要用到useRef ,它是一个 React Hook,它能帮助引用一个不需要渲染的值。然后我们可以在useEffect钩子函数中,设置这个值,并返回一个cleanup函数,这个函数执行时机就是在组件卸载的时候,因此我们可以在这个cleanup函数中修改这个值。代码如下所示:

ini 复制代码
import { useEffect, useRef } from 'react';
const useUnmountedRef = () => {
    const unMountedRef = useRef(false);
    useEffect(() => {
        unMountedRef.current = false;
        return () => {
            // 组件卸载时,设置为true,代表组件已经卸载
            unMountedRef.current = true;
        }
    },[]);
    return unMountedRef;
}

export default useUnmountedRef;

如何停止更新状态

接下来是解决第二个问题,我们将使用useState来定义状态,这个hook接收一个状态或者是一个返回状态的函数,然后我们使用useCallback定义一个函数,在函数当中去改变这个状态,在该函数当中,我们将会使用useUnmountedRef来判断当前组件是否已经卸载,如果为true,就代表不需要更改该状态,最后将状态和修改该状态的函数返回。

javascript 复制代码
import { useState,useCallback } from 'react';
import useUnmountedRef from './useUnmountedRef.tsx';
const useSafeState = <S>(state:S | () => S) => {
    // 获取组件是否卸载的状态
    const unMountedRef = useUnmountedRef();
    // 使用useState定义一个状态
    const [state, setState] = useState(state);
    // 定义更改状态的函数,接收一个更改后的状态参数,用来更改状态
    const setNewState = useCallback((newState) => {
        // 判断如果当前组件已卸载,则不执行更新状态
        if(unMountedRef.current){
            return;
        }
        setState(newState);
    },[]);
    
    // 返回状态和修改状态的函数
    return [state,setNewState] as const;
}

export default useSafeState;

注: as const 是 TypeScript 中的一个用于修饰符,它可以被用来修改类型推断的行为。

在ahooks中,还对useSafeState的类型做了增强,如下所示:

javascript 复制代码
function useSafeState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];  
function useSafeState<S = undefined>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>]; 

如何避免内存泄漏问题?

useSafeState 的设计和实现方式考虑了避免内存泄漏的问题:

  1. 使用 useUnmountedRef 来检测组件是否卸载,以停止状态更新,避免因状态更新导致的错误。

  2. 使用 useCallback 缓存函数,并自动清除缓存,以避免内存泄漏。

  3. 设计独立于其他状态的状态更新方式,确保状态仅依赖于初始状态或生成函数,有效避免内存泄漏问题。

综上所述,useSafeState 通过多种方式避免内存泄漏,包括使用 useUnmountedRef 检测组件状态,使用 useCallback 缓存函数并自动清除,以及设计独立于其他状态的状态更新方式。这些设计理念和实现方式,使得 useSafeState 能更好地管理状态,并避免了内存泄漏问题,使得React应用更加安全健壮。

应用场景

useSafeState 主要适用于以下场景:

  1. 安全状态管理:确保在组件被卸载时停止状态更新,避免错误。

  2. 独立状态更新 :用于更新与其他状态无关的变化,如通过函数生成状态或使用与 useState 类似的 API。

下面是使用useSafeState的示例:

基础用法示例

javascript 复制代码
import React, { useSafeState } from 'react';
function MyComponent() {
  const [state, setState] = useSafeState({ count: 0 });
  const incrementCount = () => {
    // 使用 set 方法来修改状态
    setState((state) => ({ count: state.count + 1 }));
  };
  return (
    <div>
      <h1>{state.count}</h1>
      <button onClick={incrementCount}>+</button>
    </div>
  );
}

在这个示例中,我们使用useSafeState来创建一个状态变量count,并提供了一个incrementCount方法来修改状态。当点击"+"按钮时,incrementCount方法会被调用,它使用setState方法来修改状态变量count的值。

自定义初始化状态示例

javascript 复制代码
import React from 'react';
import { useSafeState } from './useSafeState.tsx';

const CustomComponent = () => {
    // 获取缓存中的数据,如无则初始化一个字符串
    const [message, setMessage] = useSafeState(() => {
        const initialMessage = localStorage.getItem('message');
        return initialMessage || 'Hello, World!';
    });
    
    // 更新数据,并缓存
    const handleSaveMessage = newMessage => {
        setMessage(newMessage);
        localStorage.setItem('message', newMessage);
    };

    return (
        <div>
            <p>{message}</p>
            <button onClick={() => handleSaveMessage('New Message')}>保存消息</button>
        </div>
    );
} 

export default CustomComponent;

在这个示例中,我们使用useSafeState来创建一个状态变量message,并提供了一个handleSaveMessage方法来修改状态。当点击"保存消息"按钮时,handleSaveMessage方法会被调用,它使用setMessage方法来修改状态变量message的值,并且我们还将数据使用localStorage缓存了下来,修改的时候同样也会修改缓存的值。

总结

使用useSafeState可以帮助我们保障 React 状态的安全性,避免状态的不一致和数据的丢失。它提供了一种简单而安全的方式来管理状态,让我们可以更加自信地编写 React 组件。

相关推荐
万叶学编程2 小时前
Day02-JavaScript-Vue
前端·javascript·vue.js
前端李易安4 小时前
Web常见的攻击方式及防御方法
前端
PythonFun4 小时前
Python技巧:如何避免数据输入类型错误
前端·python
知否技术4 小时前
为什么nodejs成为后端开发者的新宠?
前端·后端·node.js
hakesashou4 小时前
python交互式命令时如何清除
java·前端·python
天涯学馆4 小时前
Next.js与NextAuth:身份验证实践
前端·javascript·next.js
HEX9CF5 小时前
【CTF Web】Pikachu xss之href输出 Writeup(GET请求+反射型XSS+javascript:伪协议绕过)
开发语言·前端·javascript·安全·网络安全·ecmascript·xss
ConardLi5 小时前
Chrome:新的滚动捕捉事件助你实现更丝滑的动画效果!
前端·javascript·浏览器
ConardLi5 小时前
安全赋值运算符,新的 JavaScript 提案让你告别 trycatch !
前端·javascript