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 组件。

相关推荐
m0_748230942 分钟前
Rust赋能前端: 纯血前端将 Table 导出 Excel
前端·rust·excel
qq_5895681010 分钟前
Echarts的高级使用,动画,交互api
前端·javascript·echarts
黑客老陈1 小时前
新手小白如何挖掘cnvd通用漏洞之存储xss漏洞(利用xss钓鱼)
运维·服务器·前端·网络·安全·web3·xss
正小安1 小时前
Vite系列课程 | 11. Vite 配置文件中 CSS 配置(Modules 模块化篇)
前端·vite
暴富的Tdy2 小时前
【CryptoJS库AES加密】
前端·javascript·vue.js
neeef_se2 小时前
Vue中使用a标签下载静态资源文件(比如excel、pdf等),纯前端操作
前端·vue.js·excel
m0_748235612 小时前
web 渗透学习指南——初学者防入狱篇
前端
z千鑫2 小时前
【前端】入门指南:Vue中使用Node.js进行数据库CRUD操作的详细步骤
前端·vue.js·node.js