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

devtools/2024/11/20 9:30:00/

目录

  • 一、官方介绍
    • 1. Sensors
    • 2. UI
    • 3. Animations
    • 4. Side-Effects
    • 5. Lifecycles
    • 6. State
    • 7. Miscellaneous
  • 二、源码学习
    • 示例:n. xx - yy
    • Lifecycles - useEffectOnce
    • Lifecycles - useEvent
    • Lifecycles - useLifecycles
    • Lifecycles - useMountedState & useUnmountPromise
      • useMountedState
      • useUnmountPromise
    • Lifecycles - usePromise
    • Lifecycles - useLogger
    • Lifecycles - useMount
    • Lifecycles - useUnmount
    • 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.

使用

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;
};

源码

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

解释

import { EffectCallback, useEffect } from 'react';
  • EffectCallback:这是 TypeScript 中的一个类型,表示传递给 useEffect 的副作用函数的类型。它是一个回调函数,通常用于定义副作用。
  • useEffect:这是 React 的一个 Hook,用于处理副作用(side effects),例如数据获取、订阅、DOM 操作等。
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.

使用

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>);
};

源码

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 进行类型检查。

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

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

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 等)。

接着看源码:

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 方法。
export type UseEventTarget = ListenerType1 | ListenerType2;
  • UseEventTarget: 联合类型,表示可以是 ListenerType1ListenerType2 中的任何一个。
const defaultTarget = isBrowser ? window : null;
  • defaultTarget: 根据 isBrowser 判断是否在浏览器环境中,如果是则默认为 window,否则为 null
const isListenerType1 = (target: any): target is ListenerType1 => {return !!target.addEventListener;
};const isListenerType2 = (target: any): target is ListenerType2 => {return !!target.on;
};
  • isListenerType1isListenerType2: 类型谓词函数,用于检查 target 是否符合 ListenerType1ListenerType2 类型。
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)。
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.

使用

import {useLifecycles} from 'react-use';const Demo = () => {useLifecycles(() => console.log('MOUNTED'), () => console.log('UNMOUNTED'));return null;
};

源码

import { useEffect } from 'react';const useLifecycles = (mount, unmount?) => {useEffect(() => {if (mount) {mount();}return () => {if (unmount) {unmount();}};}, []);
};export default useLifecycles;

解释

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

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

Lifecycles - useMountedState & useUnmountPromise

track if component is mounted.

useMountedState

使用

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

源码

// 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 的实现:

import { useCallback, useEffect, useRef } from 'react';
  • useCallback: 用于创建一个记忆化的回调函数,只有在依赖项发生变化时才会重新创建。
  • useEffect: 用于处理副作用,例如数据获取、事件监听和 DOM 操作等。
  • useRef: 用于创建一个可变的引用对象,该对象在组件的整个生命周期内保持不变。
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 的主要目的是提供一个安全的方法来检查组件的挂载状态。这样可以防止在组件卸载后进行状态更新或其他操作,从而避免潜在的内存泄漏和错误。

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

使用


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

源码

// 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 的实现和功能:

import { useMemo, useRef } from 'react';
import useEffectOnce from './useEffectOnce';
  • useMemo: 用于创建一个记忆化的值,以避免在每次渲染时都重新计算。
  • useRef: 用于创建一个可变的引用对象,该对象在组件的整个生命周期内保持不变。
  • useEffectOnce: 自定义的 Hook,确保某个副作用只在组件挂载时执行一次,并在组件卸载时执行清理逻辑。
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 的状态更新问题。这在异步操作中尤其有用,比如数据获取、计时器等场景中。

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.

使用

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);})();});
};

源码

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 来检查组件的挂载状态。

import { useCallback } from 'react';
import useMountedState from './useMountedState';
  • useCallback: 用于创建一个记忆化的回调函数,避免每次渲染时都重新创建函数。
  • useMountedState: 自定义 Hook,用于检查组件是否仍然挂载(是否处于活动状态)。
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 状态更新。这在处理异步操作(如数据获取、定时器等)时尤为重要,以避免潜在的内存泄漏和不必要的状态更新。

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.

使用

import {useLogger} from 'react-use';const Demo = (props) => {useLogger('Demo', props);return null;
};//Demo mounted {}
//Demo updated {}
//Demo unmounted

源码

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 的实现和功能。

import useEffectOnce from './useEffectOnce';
import useUpdateEffect from './useUpdateEffect';
  • useEffectOnce: 这个自定义 Hook 确保其副作用(如日志记录)只在组件挂载时执行一次,并在组件卸载时执行清理操作。
  • useUpdateEffect: 这个自定义 Hook 确保其副作用(如日志记录)只在组件更新时执行(即组件的 propsstate 发生变化时)。
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.

使用

import {useMount} from 'react-use';const Demo = () => {useMount(() => alert('MOUNTED'));return null;
};

源码

import useEffectOnce from './useEffectOnce';const useMount = (fn: () => void) => {useEffectOnce(() => {fn();});
};export default useMount;

解释

比较简单,不做解释。

Lifecycles - useUnmount

calls unmount callbacks.

使用

import {useUnmount} from 'react-use';const Demo = () => {useUnmount(() => alert('UNMOUNTED'));return null;
};

源码

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 invokedfnRef.current = fn;useEffectOnce(() => () => fnRef.current());
};export default useUnmount;

解释

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

Lifecycles - useUpdateEffect

run an effect only on updates.

使用

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 beyondreturn () => { // *OPTIONAL*// do something on unmount}}) // you can include deps array if necessaryreturn <div>Count: {count}</div>
};

源码

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 实现。

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

    • 调用 useFirstMountState Hook 来判断组件是否是第一次挂载。useFirstMountState 通常是一个自定义 Hook,其功能是返回一个布尔值,指示组件是否为第一次挂载。
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 是一个很好的选择。例如,你可能只希望在数据更新时执行某些操作,而不是在组件第一次加载时执行这些操作。

http://www.ppmy.cn/devtools/104722.html

相关文章

linux 9系统分区扩容

1.可以看到我的是9.2的系统&#xff0c;系统分区&#xff1a;/dev/mapper/rl-root 83G 8.0G 75G 10% / 2.接下来&#xff0c;我们新增一块新的硬盘&#xff0c;而不是直接对这个硬盘的基础上再扩容。 关机&#xff0c;加30G硬盘&#xff0c;再开机 fdisk -l fdisk /dev/…

APP、小程序对接聚合广告平台需要提供哪些资料?

开发者通过广告变现获得稳定的收益&#xff0c;通过在应用中展示广告获得收益&#xff0c;而广告商则能够在广告中获得曝光和用户互动。 开发者接入聚合广告SDK可以对接聚合广告平台&#xff0c;开发者可以按照提供的iOS/Android SDK 接入文档&#xff0c;完成对接。对接主要分…

用友U8接口-自定义项和扩展自定义项(6)

概括 本文的操作需要正确部署U8API保存单据时传入自定义项和扩展自定义项 单据自定义项 ERP界面 接口参数 自定义项为标准erp字段&#xff0c;以cDefine开始的字段对应接口传入参数位置 [{"Inum": "OtherIn","Data": {"iHead": {&…

比较一下React与Vue

React和Vue都是现代前端开发中广泛使用的JavaScript库&#xff0c;它们各自具有独特的特点和优势。以下是对React和Vue的比较&#xff1a; 1. 开发模式和范式 React&#xff1a; 本质是一个前端组件框架&#xff0c;由后端组件演化而来。 鼓励将UI分解为小的、独立的、可复用…

netcore高级知识点,内存对齐,原理与示例

最近几年一直从事物联网开发&#xff0c;与硬件打交道越来越多&#xff0c;发现越接近底层开发对性能的追求越高&#xff0c;毕竟硬件资源相对上层应用来实在是太缺乏了。今天想和大家一起分享关于C#中的内存对齐&#xff0c;希望通过理解和优化内存对齐&#xff0c;可以帮助大…

【GPT】Coze使用开放平台接口-【7】Dify 比较篇

个人主观&#xff0c;轻喷&#xff0c;没有什么绝对&#xff0c;只是相对 持续更新 用下来的感受是 coze 用于社交&#xff0c;dify 用来内部构建。抛开工作流&#xff0c;机器人&#xff0c;工具&#xff0c;coze 最大的区别在于可以直接发布到社交媒体上。所以&#xff0c;co…

XSS 漏洞 - 学习手册

接下来的日子我会按照目录进行笔记的更新&#xff0c;待本模块更新完毕后&#xff0c;删除此条注释。 备注&#xff1a;即使是更新完了&#xff0c;我知道也会有不足&#xff0c;所以会不定期添加或者删除或者优化某些内容&#xff0c;感兴趣的小伙伴可以关注一波。 0x01&#…

聚餐地计算(华为od机考题)

一、题目 1.原题 小华和小为是很要好的朋友&#xff0c;他们约定周末一起吃饭。 通过手机交流&#xff0c; 他们在地图上选择了多个聚餐地点 &#xff08;由于自然地形等原因&#xff0c;部分聚餐地点不可达&#xff09;&#xff0c; 求小华和小为都能到达的聚餐地点有多少个…