useSafeState-保障 React 状态的安全性

useSafeState-保障 React 状态的安全性

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

简介

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

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

相关推荐
wangruofeng4 小时前
AI Models 1.1.0 发布,查大模型价格、对比模型和找 API 地址
前端·aigc·ai编程
wangruofeng4 小时前
给前端开发者的 100 个设计参考网站
前端·设计
星栈7 小时前
视频即代码,Remotion 能做什么、不能做什么
前端·音视频开发
广州华水科技7 小时前
单北斗GNSS位移监测系统的应用与技术解析
前端
用户47949283569158 小时前
从一句话或代码仓到可交互 HTML:开源 Agent 画图技能 Archify v3.2 实测与选型边界
前端·后端·面试
风骏时光牛马10 小时前
生成一个AI多模态标题,仅输出一条标题,无额外文字、问句、备选、配图、文案等任何附加内容,不需要其他的任务服务 只要标题。
前端
wangruofeng11 小时前
从 GIF 到 AVIF:网页图片格式演进与 2026 年选型指南
前端·架构
清晨Coding12 小时前
为什么用 React Developer Tools 查看组件树这么乱?
前端·react.js
鶴哥只手遮天12 小时前
《Electron 45.2.3 内核解剖》第 4 章 补丁体系:380 个补丁改了什么
前端·electron
FungLeo14 小时前
成为全栈·Vue 管理后台篇·分类树与标签管理:层级数据如何落到管理界面
前端·javascript·vue.js·分类树·naive ui·标签管理·前端数据结构