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

相关推荐
喵叔哟31 分钟前
重构代码之取消临时字段
java·前端·重构
还是大剑师兰特1 小时前
D3的竞品有哪些,D3的优势,D3和echarts的对比
前端·javascript·echarts
王解1 小时前
【深度解析】CSS工程化全攻略(1)
前端·css
一只小白菜~1 小时前
web浏览器环境下使用window.open()打开PDF文件不是预览,而是下载文件?
前端·javascript·pdf·windowopen预览pdf
方才coding1 小时前
1小时构建Vue3知识体系之vue的生命周期函数
前端·javascript·vue.js
阿征学IT1 小时前
vue过滤器初步使用
前端·javascript·vue.js
王哲晓1 小时前
第四十五章 Vue之Vuex模块化创建(module)
前端·javascript·vue.js
丶21361 小时前
【WEB】深入理解 CORS(跨域资源共享):原理、配置与常见问题
前端·架构·web
发现你走远了1 小时前
『VUE』25. 组件事件与v-model(详细图文注释)
前端·javascript·vue.js
Mr.咕咕2 小时前
Django 搭建数据管理web——商品管理
前端·python·django