WHAT - 通过 react-use 源码学习 React(Lifecycles 篇)

目录

  • 一、官方介绍
    • [1. Sensors](#1. Sensors)
    • [2. UI](#2. UI)
    • [3. Animations](#3. Animations)
    • [4. Side-Effects](#4. Side-Effects)
    • [5. Lifecycles](#5. Lifecycles)
    • [6. State](#6. State)
    • [7. Miscellaneous](#7. Miscellaneous)
  • 二、源码学习
    • [示例:n. xx - yy](#示例:n. xx - yy)
    • [Lifecycles - useEffectOnce](#Lifecycles - useEffectOnce)
    • [Lifecycles - useEvent](#Lifecycles - useEvent)
    • [Lifecycles - useLifecycles](#Lifecycles - useLifecycles)
    • [Lifecycles - useMountedState & useUnmountPromise](#Lifecycles - useMountedState & useUnmountPromise)
    • [Lifecycles - usePromise](#Lifecycles - usePromise)
    • [Lifecycles - useLogger](#Lifecycles - useLogger)
    • [Lifecycles - useMount](#Lifecycles - useMount)
    • [Lifecycles - useUnmount](#Lifecycles - useUnmount)
    • [Lifecycles - useUpdateEffect](#Lifecycles - useUpdateEffect)

一、官方介绍

Github 地址

react-use 是一个流行的 React 自定义 Hook 库,提供了一组常用的 Hook,以帮助开发者在 React 应用程序中更方便地处理常见的任务和功能。

官方将 react-use 的 Hook 分成了以下几个主要类别,以便更好地组织和查找常用的功能。每个类别涵盖了不同类型的 Hook,满足各种开发需求。以下是这些类别的详细说明:

1. Sensors

  • 功能: 主要涉及与浏览器或用户交互相关的传感器功能。
  • 示例 :
    • useMouse: 获取鼠标位置。
    • useWindowSize: 获取窗口尺寸。
    • useBattery: 监控电池状态。

2. UI

  • 功能: 涉及用户界面相关的功能,如处理样式、显示和隐藏元素等。
  • 示例 :
    • useClickAway: 监听点击事件以检测用户点击是否发生在组件外部。
    • useMeasure: 测量元素的大小和位置。
    • useDarkMode: 管理和检测暗模式状态。

3. Animations

  • 功能: 处理动画和过渡效果。
  • 示例 :
    • useSpring : 使用 react-spring 处理动画效果。
    • useTransition : 使用 react-spring 处理过渡动画。

4. Side-Effects

  • 功能: 处理副作用相关的 Hook,包括数据获取、异步操作等。
  • 示例 :
    • useAsync: 处理异步操作,如数据获取,并提供状态和结果。
    • useFetch: 简化数据获取操作。
    • useAxios: 使用 Axios 进行数据请求的 Hook。

5. Lifecycles

  • 功能: 处理组件生命周期相关的 Hook。
  • 示例 :
    • useMount: 在组件挂载时执行的 Hook。
    • useUnmount: 在组件卸载时执行的 Hook。
    • useUpdate: 在组件更新时执行的 Hook。

6. State

  • 功能: 管理组件状态和相关逻辑。
  • 示例 :
    • useState: 提供基本状态管理功能。
    • useReducer : 替代 useState 实现更复杂的状态逻辑。
    • useForm: 管理表单状态和验证。
    • useInput: 管理输入字段的状态。

7. Miscellaneous

  • 功能: 各种其他实用功能的 Hook,涵盖一些不容易归类到其他类别的功能。

这种分类方法使得 react-use 的 Hook 更加有组织和易于查找,帮助开发者快速找到需要的功能并有效地集成到他们的应用程序中。

二、源码学习

示例:n. xx - yy

something

使用

源码

解释

Lifecycles - useEffectOnce

a modified useEffect hook that only runs once.

使用

typescript 复制代码
import {useEffectOnce} from 'react-use';

const Demo = () => {
  useEffectOnce(() => {
    console.log('Running effect once on mount')

    return () => {
      console.log('Running clean-up of effect on unmount')
    }
  });

  return null;
};

源码

typescript 复制代码
import { EffectCallback, useEffect } from 'react';
const useEffectOnce = (effect: EffectCallback) => {
  useEffect(effect, []);
};
export default useEffectOnce;

解释

javascript 复制代码
import { EffectCallback, useEffect } from 'react';
  • EffectCallback :这是 TypeScript 中的一个类型,表示传递给 useEffect 的副作用函数的类型。它是一个回调函数,通常用于定义副作用。
  • useEffect:这是 React 的一个 Hook,用于处理副作用(side effects),例如数据获取、订阅、DOM 操作等。
javascript 复制代码
const useEffectOnce = (effect: EffectCallback) => {
  useEffect(effect, []);
};
  • useEffectOnce :这是一个自定义 Hook,接收一个副作用函数 effect 作为参数。自定义 Hook 是函数,它可以调用其他 Hook 并返回值。

  • useEffect(effect, []) :调用了 React 的 useEffect Hook。useEffect 的第一个参数是副作用函数 effect,第二个参数是依赖数组(dependencies array)。

    • 副作用函数 effect:当组件挂载时执行,并在组件更新或卸载时执行清理(如果副作用函数返回一个清理函数)。

    • 依赖数组 [] :这个数组定义了副作用函数的依赖项。空数组 [] 表示副作用函数只在组件挂载时执行一次。这是因为 useEffect 只有在依赖数组中的值发生变化时才会重新执行副作用函数,但由于这里依赖数组为空,副作用函数仅在组件初次渲染时执行一次。

Lifecycles - useEvent

subscribe to events.

使用

typescript 复制代码
import {useEvent, useList} from 'react-use';

const Demo = () => {
  const [list, {push, clear}] = useList();

  const onKeyDown = useCallback(({key}) => {
    if (key === 'r') clear();
    push(key);
  }, []);

  useEvent('keydown', onKeyDown);

  return (
    <div>
      <p>
        Press some keys on your keyboard, <code style={{color: 'tomato'}}>r</code> key resets the list
      </p>
      <pre>
        {JSON.stringify(list, null, 4)}
      </pre>
    </div>
  );
};

源码

typescript 复制代码
import { useEffect } from 'react';
import { isBrowser, off, on } from './misc/util';

export interface ListenerType1 {
  addEventListener(name: string, handler: (event?: any) => void, ...args: any[]);

  removeEventListener(name: string, handler: (event?: any) => void, ...args: any[]);
}

export interface ListenerType2 {
  on(name: string, handler: (event?: any) => void, ...args: any[]);

  off(name: string, handler: (event?: any) => void, ...args: any[]);
}

export type UseEventTarget = ListenerType1 | ListenerType2;

const defaultTarget = isBrowser ? window : null;

const isListenerType1 = (target: any): target is ListenerType1 => {
  return !!target.addEventListener;
};
const isListenerType2 = (target: any): target is ListenerType2 => {
  return !!target.on;
};

type AddEventListener<T> = T extends ListenerType1
  ? T['addEventListener']
  : T extends ListenerType2
  ? T['on']
  : never;

export type UseEventOptions<T> = Parameters<AddEventListener<T>>[2];

const useEvent = <T extends UseEventTarget>(
  name: Parameters<AddEventListener<T>>[0],
  handler?: null | undefined | Parameters<AddEventListener<T>>[1],
  target: null | T | Window = defaultTarget,
  options?: UseEventOptions<T>
) => {
  useEffect(() => {
    if (!handler) {
      return;
    }
    if (!target) {
      return;
    }
    if (isListenerType1(target)) {
      on(target, name, handler, options);
    } else if (isListenerType2(target)) {
      target.on(name, handler, options);
    }
    return () => {
      if (isListenerType1(target)) {
        off(target, name, handler, options);
      } else if (isListenerType2(target)) {
        target.off(name, handler, options);
      }
    };
  }, [name, handler, target, JSON.stringify(options)]);
};

export default useEvent;

解释

这个 Hook 旨在帮助处理事件监听的操作,它支持两种不同的事件监听接口,并且使用 TypeScript 进行类型检查。

javascript 复制代码
import { useEffect } from 'react';
import { isBrowser, off, on } from './misc/util';
  • useEffect: 用于处理副作用。
  • isBrowser : 可能是一个布尔值,用于检查是否在浏览器环境中。具体实现可以阅读 misc/util.ts
  • offon : 这些可能是自定义的工具函数,用于添加和移除事件监听器。具体实现可以阅读 misc/util.ts

这里贴出来 on 的具体实现:

typescript 复制代码
export function on<T extends Window | Document | HTMLElement | EventTarget>(
  obj: T | null,
  ...args: Parameters<T['addEventListener']> | [string, Function | null, ...any]
): void {
  if (obj && obj.addEventListener) {
    obj.addEventListener(...(args as Parameters<HTMLElement['addEventListener']>));
  }
}

其中类型 [string, Function | null, ...any] 是一个元组类型(Tuple Type)定义,它描述了一个具有特定结构的数组。元组(Tuple) 是 TypeScript 中的一个数据结构,用于表示固定长度和已知元素类型的数组。每个元素的类型可以不同,并且可以访问每个元素。...any 使用了 TypeScript 的 Rest Parameters,表示在元组的前两个元素之后,可以有零个或多个任意类型的元素。any 类型意味着这些元素可以是任何类型(number、string、boolean、object 等)。

接着看源码:

typescript 复制代码
export interface ListenerType1 {
  addEventListener(name: string, handler: (event?: any) => void, ...args: any[]);
  removeEventListener(name: string, handler: (event?: any) => void, ...args: any[]);
}

export interface ListenerType2 {
  on(name: string, handler: (event?: any) => void, ...args: any[]);
  off(name: string, handler: (event?: any) => void, ...args: any[]);
}
  • ListenerType1ListenerType2 : 定义了两种不同的事件监听接口。
    • ListenerType1 使用 addEventListenerremoveEventListener 方法。
    • ListenerType2 使用 onoff 方法。
typescript 复制代码
export type UseEventTarget = ListenerType1 | ListenerType2;
  • UseEventTarget : 联合类型,表示可以是 ListenerType1ListenerType2 中的任何一个。
typescript 复制代码
const defaultTarget = isBrowser ? window : null;
  • defaultTarget : 根据 isBrowser 判断是否在浏览器环境中,如果是则默认为 window,否则为 null
typescript 复制代码
const isListenerType1 = (target: any): target is ListenerType1 => {
  return !!target.addEventListener;
};

const isListenerType2 = (target: any): target is ListenerType2 => {
  return !!target.on;
};
  • isListenerType1isListenerType2 : 类型谓词函数,用于检查 target 是否符合 ListenerType1ListenerType2 类型。
typescript 复制代码
type AddEventListener<T> = T extends ListenerType1
  ? T['addEventListener']
  : T extends ListenerType2
  ? T['on']
  : never;

export type UseEventOptions<T> = Parameters<AddEventListener<T>>[2];
  • AddEventListener : 条件类型,根据 T 的类型决定是 addEventListener 还是 on 方法。
  • UseEventOptions : 提取 AddEventListener 的第三个参数类型,这通常是事件监听选项(如 capture)。
typescript 复制代码
const useEvent = <T extends UseEventTarget>(
  name: Parameters<AddEventListener<T>>[0],
  handler?: null | undefined | Parameters<AddEventListener<T>>[1],
  target: null | T | Window = defaultTarget,
  options?: UseEventOptions<T>
) => {
  useEffect(() => {
    if (!handler) {
      return;
    }
    if (!target) {
      return;
    }
    if (isListenerType1(target)) {
      on(target, name, handler, options);
    } else if (isListenerType2(target)) {
      target.on(name, handler, options);
    }
    return () => {
      if (isListenerType1(target)) {
        off(target, name, handler, options);
      } else if (isListenerType2(target)) {
        target.off(name, handler, options);
      }
    };
  }, [name, handler, target, JSON.stringify(options)]);
};
  • 参数:

    • name : 事件名称,如 'click''scroll'
    • handler: 事件处理函数。
    • target : 事件目标,可以是 nullListenerType1ListenerType2 类型的对象,也可以是 window
    • options : 事件选项,可能包括 { capture: boolean } 等。
  • useEffect : 用于处理副作用,添加和移除事件监听器。依赖数组包含 namehandlertargetoptions(序列化为字符串,以防 options 是一个对象)。

    • 添加事件监听器:

      • isListenerType1(target) : 如果 targetListenerType1 类型,使用 on 函数。
      • isListenerType2(target) : 如果 targetListenerType2 类型,使用 target.on 方法。
    • 移除事件监听器:

      • 在副作用清理函数中,使用 offtarget.off 方法。

为什么要区分 on 和 addEventListener?在 JavaScript 和前端开发中,on 和 addEventListener 是两种不同的事件处理机制,addEventListener 是标准的 DOM API 方法,用于在 DOM 元素上注册事件监听器。它允许:1. 支持多次添加同一事件类型的监听器 2. 支持选项参数,例如 capture(是否在捕获阶段调用),once(是否只调用一次),passive(是否可以为被动监听器)。on 是许多 JavaScript 框架、库或自定义事件系统中用于注册事件监听的惯用方法,它的实现可能会因库而异。

Lifecycles - useLifecycles

calls mount and unmount callbacks.

使用

typescript 复制代码
import {useLifecycles} from 'react-use';

const Demo = () => {
  useLifecycles(() => console.log('MOUNTED'), () => console.log('UNMOUNTED'));
  return null;
};

源码

typescript 复制代码
import { useEffect } from 'react';

const useLifecycles = (mount, unmount?) => {
  useEffect(() => {
    if (mount) {
      mount();
    }
    return () => {
      if (unmount) {
        unmount();
      }
    };
  }, []);
};

export default useLifecycles;

解释

useLifecycles 是一个自定义的 React Hook,它简化了组件生命周期管理,特别是挂载(mount)和卸载(unmount)时的操作。它是一个实用的工具,可以帮助你在函数组件中更方便地处理副作用的生命周期。

javascript 复制代码
const useLifecycles = (mount, unmount?) => {};
  • 参数 :
    • mount : 一个函数,当组件挂载时执行。如果没有提供 mount 函数,组件挂载时不会有额外操作。
    • unmount (可选): 一个函数,当组件卸载时执行。如果没有提供 unmount 函数,组件卸载时不会有额外操作。

Lifecycles - useMountedState & useUnmountPromise

track if component is mounted.

useMountedState

使用

typescript 复制代码
import * as React from 'react';
import {useMountedState} from 'react-use';
const Demo = () => {
  const isMounted = useMountedState();
  React.useEffect(() => {
    setTimeout(() => {
      if (isMounted()) {
        // ...
      } else {
        // ...
      }
    }, 1000);
  });
};

源码

typescript 复制代码
// useMountedState
import { useCallback, useEffect, useRef } from 'react';
export default function useMountedState(): () => boolean {
  const mountedRef = useRef<boolean>(false);
  const get = useCallback(() => mountedRef.current, []);

  useEffect(() => {
    mountedRef.current = true;

    return () => {
      mountedRef.current = false;
    };
  }, []);

  return get;
}

解释

useMountedState 是一个自定义的 React Hook,用于检测组件的挂载状态。它的主要作用是提供一个函数,这个函数可以用来检查组件是否仍然挂载在 DOM 中。这个自定义 Hook 可以在异步操作或副作用中非常有用,以避免在组件卸载后执行状态更新或其他操作。

让我们逐步解析这个 useMountedState Hook 的实现:

javascript 复制代码
import { useCallback, useEffect, useRef } from 'react';
  • useCallback: 用于创建一个记忆化的回调函数,只有在依赖项发生变化时才会重新创建。
  • useEffect: 用于处理副作用,例如数据获取、事件监听和 DOM 操作等。
  • useRef: 用于创建一个可变的引用对象,该对象在组件的整个生命周期内保持不变。
javascript 复制代码
export default function useMountedState(): () => boolean {
  const mountedRef = useRef<boolean>(false);
  const get = useCallback(() => mountedRef.current, []);

  useEffect(() => {
    mountedRef.current = true;

    return () => {
      mountedRef.current = false;
    };
  }, []);

  return get;
}
  1. const mountedRef = useRef<boolean>(false);

    • 使用 useRef 创建一个可变的引用 mountedRef,其初始值为 false。这个引用对象会在整个组件生命周期内保持不变。
    • mountedRef.current 用于存储组件的挂载状态。
  2. const get = useCallback(() => mountedRef.current, []);

    • 使用 useCallback 创建一个记忆化的回调函数 get,该函数返回 mountedRef.current 的值。
    • 空的依赖数组 [] 表示 get 函数不会因组件重新渲染而重新创建。这样可以确保每次返回的 get 函数引用相同,避免不必要的重新渲染。
  3. useEffect(() => { ... }, []);

    • useEffect 用于执行副作用,在组件挂载时执行并在组件卸载时清理。
    • mountedRef.current = true;:当组件挂载时,将 mountedRef.current 设置为 true
    • return () => { mountedRef.current = false; };:在组件卸载时,将 mountedRef.current 设置为 false,以便清理。
  4. return get;

    • 返回 get 函数,它会根据 mountedRef.current 的值返回组件是否仍然挂载。

useMountedState Hook 的主要目的是提供一个安全的方法来检查组件的挂载状态。这样可以防止在组件卸载后进行状态更新或其他操作,从而避免潜在的内存泄漏和错误。

javascript 复制代码
import React, { useEffect } from 'react';
import useMountedState from './useMountedState';

const MyComponent: React.FC = () => {
  const isMounted = useMountedState();

  useEffect(() => {
    const fetchData = async () => {
      const data = await fetch('/api/data');
      if (isMounted()) {
        // 只有在组件仍然挂载时才更新状态
        // setData(data);
      }
    };

    fetchData();

    // 清理函数(如果需要)
    return () => {
      // 处理组件卸载时的清理逻辑
    };
  }, [isMounted]);

  return <div>My Component</div>;
};

在这个示例中,isMounted() 函数用于检查组件是否仍然挂载。这样可以确保在组件已经卸载后不会尝试更新状态,从而避免错误。

useUnmountPromise

使用

typescript 复制代码
import useUnmountPromise from 'react-use/lib/useUnmountPromise';
const Demo = () => {
  const mounted = useUnmountPromise();
  useEffect(async () => {
    await mounted(someFunction()); // Will not resolve if component un-mounts.
  });
};

源码

typescript 复制代码
// useUnmountPromise
import { useMemo, useRef } from 'react';
import useEffectOnce from './useEffectOnce';

export type Race = <P extends Promise<any>, E = any>(promise: P, onError?: (error: E) => void) => P;

const useUnmountPromise = (): Race => {
  const refUnmounted = useRef(false);
  useEffectOnce(() => () => {
    refUnmounted.current = true;
  });

  const wrapper = useMemo(() => {
    const race = <P extends Promise<any>, E>(promise: P, onError?: (error: E) => void) => {
      const newPromise: P = new Promise((resolve, reject) => {
        promise.then(
          (result) => {
            if (!refUnmounted.current) resolve(result);
          },
          (error) => {
            if (!refUnmounted.current) reject(error);
            else if (onError) onError(error);
            else console.error('useUnmountPromise', error);
          }
        );
      }) as P;
      return newPromise;
    };
    return race;
  }, []);

  return wrapper;
};

export default useUnmountPromise;

解释

useUnmountPromise 是一个自定义的 React Hook,旨在帮助处理在组件卸载后的 Promise 状态。它确保在组件卸载时不会对未完成的 Promise 进行状态更新,从而避免潜在的内存泄漏和错误。

让我们详细解析这个 Hook 的实现和功能:

javascript 复制代码
import { useMemo, useRef } from 'react';
import useEffectOnce from './useEffectOnce';
  • useMemo: 用于创建一个记忆化的值,以避免在每次渲染时都重新计算。
  • useRef: 用于创建一个可变的引用对象,该对象在组件的整个生命周期内保持不变。
  • useEffectOnce: 自定义的 Hook,确保某个副作用只在组件挂载时执行一次,并在组件卸载时执行清理逻辑。
javascript 复制代码
const useUnmountPromise = (): Race => {
  const refUnmounted = useRef(false);
  useEffectOnce(() => () => {
    refUnmounted.current = true;
  });

  const wrapper = useMemo(() => {
    const race = <P extends Promise<any>, E>(promise: P, onError?: (error: E) => void) => {
      const newPromise: P = new Promise((resolve, reject) => {
        promise.then(
          (result) => {
            if (!refUnmounted.current) resolve(result);
          },
          (error) => {
            if (!refUnmounted.current) reject(error);
            else if (onError) onError(error);
            else console.error('useUnmountPromise', error);
          }
        );
      }) as P;
      return newPromise;
    };
    return race;
  }, []);

  return wrapper;
};
  1. const refUnmounted = useRef(false);

    • 使用 useRef 创建一个引用 refUnmounted,初始值为 false,用于跟踪组件的卸载状态。
  2. useEffectOnce(() => () => { refUnmounted.current = true; });

    • useEffectOnce 确保副作用只在组件挂载时执行一次,并在组件卸载时执行清理。
    • 当组件卸载时,将 refUnmounted.current 设置为 true。这样,后续的 Promise 处理可以检查组件是否已经卸载。
  3. const wrapper = useMemo(() => { ... }, []);

    • useMemo 用于创建一个记忆化的 race 函数,避免每次渲染时都重新创建。
  4. const race = <P extends Promise<any>, E>(promise: P, onError?: (error: E) => void) => { ... }

    • race 函数 : 这个函数接受一个 Promise 和一个可选的错误处理函数 onError。它创建并返回一个新的 Promise。
      • then 方法的成功回调中,检查 refUnmounted.current 是否为 false,如果是,则解析结果。
      • then 方法的失败回调中,检查 refUnmounted.current 是否为 false,如果是,则拒绝错误;如果组件已经卸载,则调用 onError 处理错误,或者在控制台记录错误。
  5. return wrapper;

    • 返回 wrapper,这是一个记忆化的 race 函数。

useUnmountPromise Hook 的主要目的是处理组件卸载后 Promise 的状态更新问题。这在异步操作中尤其有用,比如数据获取、计时器等场景中。

javascript 复制代码
import React, { useEffect, useState } from 'react';
import useUnmountPromise from './useUnmountPromise';

const MyComponent: React.FC = () => {
  const race = useUnmountPromise();
  const [data, setData] = useState<string | null>(null);

  useEffect(() => {
    const fetchData = async () => {
      const promise = fetch('/api/data').then(response => response.json());
      const result = await race(promise);
      setData(result);
    };

    fetchData();

    // 清理函数(如果需要)
    return () => {
      // 处理组件卸载时的清理逻辑
    };
  }, [race]);

  return <div>{data ? data : 'Loading...'}</div>;
};

在这个示例中,race 函数用于确保在组件卸载后,如果 fetch 请求未完成,不会对组件状态进行更新,从而避免潜在的错误。

Lifecycles - usePromise

resolves promise only while component is mounted.

使用

typescript 复制代码
import {usePromise} from 'react-use';

const Demo = ({promise}) => {
  const mounted = usePromise();
  const [value, setValue] = useState();

  useEffect(() => {
    (async () => {
      const value = await mounted(promise);
      // This line will not execute if <Demo> component gets unmounted.
      setValue(value);
    })();
  });
};

源码

typescript 复制代码
import { useCallback } from 'react';
import useMountedState from './useMountedState';

export type UsePromise = () => <T>(promise: Promise<T>) => Promise<T>;

const usePromise: UsePromise = () => {
  const isMounted = useMountedState();
  return useCallback(
    (promise: Promise<any>) =>
      new Promise<any>((resolve, reject) => {
        const onValue = (value) => {
          isMounted() && resolve(value);
        };
        const onError = (error) => {
          isMounted() && reject(error);
        };
        promise.then(onValue, onError);
      }),
    []
  );
};

export default usePromise;

解释

usePromise 是一个自定义的 React Hook,旨在处理异步 Promise 操作,同时确保在组件卸载后不会对已卸载的组件进行状态更新。这个 Hook 使用了另一个自定义 Hook useMountedState 来检查组件的挂载状态。

javascript 复制代码
import { useCallback } from 'react';
import useMountedState from './useMountedState';
  • useCallback: 用于创建一个记忆化的回调函数,避免每次渲染时都重新创建函数。
  • useMountedState: 自定义 Hook,用于检查组件是否仍然挂载(是否处于活动状态)。
javascript 复制代码
const usePromise: UsePromise = () => {
  const isMounted = useMountedState();
  return useCallback(
    (promise: Promise<any>) =>
      new Promise<any>((resolve, reject) => {
        const onValue = (value) => {
          isMounted() && resolve(value);
        };
        const onError = (error) => {
          isMounted() && reject(error);
        };
        promise.then(onValue, onError);
      }),
    []
  );
};
  1. const isMounted = useMountedState();

    • 调用 useMountedState Hook 获取 isMounted 函数,这个函数用于检查组件是否仍然挂载。
  2. return useCallback((promise: Promise<any>) => { ... }, []);

    • 使用 useCallback 创建一个记忆化的函数,只有在依赖项(在此情况下为空数组 [])发生变化时才会重新创建。
    • useCallback 确保这个回调函数在每次组件渲染时保持稳定的引用,从而避免不必要的重新渲染。
  3. new Promise<any>((resolve, reject) => { ... })

    • 返回一个新的 Promise,该 Promise 封装了传入的 promise
    • onValue 函数 :在 promise 成功时调用。如果组件仍然挂载(通过 isMounted() 检查),则解析 Promise。
    • onError 函数 :在 promise 失败时调用。如果组件仍然挂载,则拒绝 Promise;如果组件已经卸载,可以选择执行错误处理函数(但在这里,错误会被直接抛出到控制台)。

usePromise Hook 的主要用途是确保在组件卸载后不会对已卸载的组件进行 Promise 状态更新。这在处理异步操作(如数据获取、定时器等)时尤为重要,以避免潜在的内存泄漏和不必要的状态更新。

javascript 复制代码
import React, { useEffect, useState } from 'react';
import usePromise from './usePromise';

const MyComponent: React.FC = () => {
  const handlePromise = usePromise();
  const [data, setData] = useState<string | null>(null);

  useEffect(() => {
    const fetchData = async () => {
      const promise = fetch('/api/data').then(response => response.json());
      try {
        const result = await handlePromise(promise);
        setData(result);
      } catch (error) {
        console.error('Failed to fetch data:', error);
      }
    };

    fetchData();

    // 清理函数(如果需要)
    return () => {
      // 处理组件卸载时的清理逻辑
    };
  }, [handlePromise]);

  return <div>{data ? data : 'Loading...'}</div>;
};

在这个示例中,handlePromise 用于处理异步 fetch 请求,确保在组件卸载后不会尝试更新组件状态。这样可以避免因为组件卸载导致的错误或警告。

对比 usePromise 和 useUnmountPromise

  1. usePromise
    功能: 封装 Promise,确保只在组件挂载时更新状态。适用场景: 需要确保 Promise 操作的回调仅在组件挂载时执行,适用于简单的 Promise 封装和状态更新。
  2. useUnmountPromise
    功能: 包装 Promise,确保在组件卸载后不会更新状态,并在组件卸载后处理错误。适用场景: 需要处理未完成的 Promise 并进行错误处理,适用于复杂的异步操作和错误处理。

Lifecycles - useLogger

logs in console as component goes through life-cycles.

使用

typescript 复制代码
import {useLogger} from 'react-use';

const Demo = (props) => {
  useLogger('Demo', props);
  return null;
};

//Demo mounted {}
//Demo updated {}
//Demo unmounted

源码

typescript 复制代码
import useEffectOnce from './useEffectOnce';
import useUpdateEffect from './useUpdateEffect';

const useLogger = (componentName: string, ...rest) => {
  useEffectOnce(() => {
    console.log(`${componentName} mounted`, ...rest);
    return () => console.log(`${componentName} unmounted`);
  });

  useUpdateEffect(() => {
    console.log(`${componentName} updated`, ...rest);
  });
};

export default useLogger;

解释

useLogger 是一个自定义的 React Hook,用于在组件的生命周期内记录日志。这种 Hook 可以帮助开发人员在开发过程中调试组件的挂载、更新和卸载行为。它利用了两个其他的自定义 Hook:useEffectOnceuseUpdateEffect。下面我们详细解析 useLogger 的实现和功能。

javascript 复制代码
import useEffectOnce from './useEffectOnce';
import useUpdateEffect from './useUpdateEffect';
  • useEffectOnce: 这个自定义 Hook 确保其副作用(如日志记录)只在组件挂载时执行一次,并在组件卸载时执行清理操作。
  • useUpdateEffect : 这个自定义 Hook 确保其副作用(如日志记录)只在组件更新时执行(即组件的 propsstate 发生变化时)。
javascript 复制代码
const useLogger = (componentName: string, ...rest) => {
  useEffectOnce(() => {
    console.log(`${componentName} mounted`, ...rest);
    return () => console.log(`${componentName} unmounted`);
  });

  useUpdateEffect(() => {
    console.log(`${componentName} updated`, ...rest);
  });
};

export default useLogger;

useLogger 可以帮助开发人员在组件的生命周期内追踪状态和行为。这对于调试和监控组件的生命周期非常有用。

Lifecycles - useMount

calls mount callbacks.

使用

typescript 复制代码
import {useMount} from 'react-use';

const Demo = () => {
  useMount(() => alert('MOUNTED'));
  return null;
};

源码

typescript 复制代码
import useEffectOnce from './useEffectOnce';

const useMount = (fn: () => void) => {
  useEffectOnce(() => {
    fn();
  });
};

export default useMount;

解释

比较简单,不做解释。

Lifecycles - useUnmount

calls unmount callbacks.

使用

typescript 复制代码
import {useUnmount} from 'react-use';

const Demo = () => {
  useUnmount(() => alert('UNMOUNTED'));
  return null;
};

源码

typescript 复制代码
import { useRef } from 'react';
import useEffectOnce from './useEffectOnce';

const useUnmount = (fn: () => any): void => {
  const fnRef = useRef(fn);

  // update the ref each render so if it change the newest callback will be invoked
  fnRef.current = fn;

  useEffectOnce(() => () => fnRef.current());
};

export default useUnmount;

解释

比较简单,不做解释。这里与 useMount 的一个区别在于,useUnMount 传入的回调可能发生变更,因此使用 useRef 来维护。

Lifecycles - useUpdateEffect

run an effect only on updates.

使用

typescript 复制代码
import React from 'react'
import {useUpdateEffect} from 'react-use';

const Demo = () => {
  const [count, setCount] = React.useState(0);
  
  React.useEffect(() => {
    const interval = setInterval(() => {
      setCount(count => count + 1)
    }, 1000)
    
    return () => {
      clearInterval(interval)
    }
  }, [])
  
  useUpdateEffect(() => {
    console.log('count', count) // will only show 1 and beyond
    
    return () => { // *OPTIONAL*
      // do something on unmount
    }
  }) // you can include deps array if necessary

  return <div>Count: {count}</div>
};

源码

typescript 复制代码
import { useEffect } from 'react';
import { useFirstMountState } from './useFirstMountState';

const useUpdateEffect: typeof useEffect = (effect, deps) => {
  const isFirstMount = useFirstMountState();

  useEffect(() => {
    if (!isFirstMount) {
      return effect();
    }
  }, deps);
};

export default useUpdateEffect;

解释

useUpdateEffect 是一个自定义的 React Hook,用于在组件更新时执行副作用(effect),而在组件首次挂载时不会执行。这可以帮助你在组件更新时运行某些逻辑,但避免在初次渲染时运行逻辑。这个 Hook 基于 useEffect 和另一个自定义 Hook useFirstMountState 实现。

javascript 复制代码
import { useEffect } from 'react';
import { useFirstMountState } from './useFirstMountState';
  • useEffect: React 的内置 Hook,用于在组件渲染后执行副作用。
  • useFirstMountState: 自定义 Hook,用于检测组件是否为第一次挂载。
javascript 复制代码
const useUpdateEffect: typeof useEffect = (effect, deps) => {
  const isFirstMount = useFirstMountState();

  useEffect(() => {
    if (!isFirstMount) {
      return effect();
    }
  }, deps);
};
  1. const isFirstMount = useFirstMountState();

    • 调用 useFirstMountState Hook 来判断组件是否是第一次挂载。useFirstMountState 通常是一个自定义 Hook,其功能是返回一个布尔值,指示组件是否为第一次挂载。
typescript 复制代码
import { useRef } from 'react';

export function useFirstMountState(): boolean {
  const isFirst = useRef(true);

  if (isFirst.current) {
    isFirst.current = false;

    return true;
  }

  return isFirst.current;
}
  • useRef : 创建一个持久化的引用对象 isFirstMount,其初始值为 true。在组件首次挂载时,这个引用的值为 true。在挂载完成后,它会被设置为 false,表示组件不再是第一次挂载。
  1. useEffect(() => { if (!isFirstMount) { return effect(); } }, deps);

    • useEffect : 在组件渲染后执行副作用。在这里,它的依赖项数组是 deps,只有当这些依赖项发生变化时,useEffect 才会重新执行。

    • if (!isFirstMount) { return effect(); } : 这个条件检查组件是否为第一次挂载。如果不是第一次挂载(即 isFirstMountfalse),则执行传入的副作用函数 effect。如果是第一次挂载,什么都不做。

useUpdateEffect 主要用于以下场景:

  • 在组件更新时执行副作用 : 当你只希望在组件更新时运行某些逻辑,而不希望在组件首次挂载时运行这些逻辑时,useUpdateEffect 是一个很好的选择。例如,你可能只希望在数据更新时执行某些操作,而不是在组件第一次加载时执行这些操作。
相关推荐
桂月二二34 分钟前
探索前端开发中的 Web Vitals —— 提升用户体验的关键技术
前端·ux
hunter2062062 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
qzhqbb2 小时前
web服务器 网站部署的架构
服务器·前端·架构
刻刻帝的海角2 小时前
CSS 颜色
前端·css
浪浪山小白兔3 小时前
HTML5 新表单属性详解
前端·html·html5
lee5763 小时前
npm run dev 时直接打开Chrome浏览器
前端·chrome·npm
2401_897579653 小时前
AI赋能Flutter开发:ScriptEcho助你高效构建跨端应用
前端·人工智能·flutter
光头程序员4 小时前
grid 布局react组件可以循数据自定义渲染某个数据 ,或插入某些数据在某个索引下
javascript·react.js·ecmascript
limit for me4 小时前
react上增加错误边界 当存在错误时 不会显示白屏
前端·react.js·前端框架
浏览器爱好者4 小时前
如何构建一个简单的React应用?
前端·react.js·前端框架