WHAT - 通过 react-use 源码学习 React(Lifecycles 篇)
目录
- 一、官方介绍
- 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.tsoff
和on
: 这些可能是自定义的工具函数,用于添加和移除事件监听器。具体实现可以阅读 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[]);
}
ListenerType1
和ListenerType2
: 定义了两种不同的事件监听接口。ListenerType1
使用addEventListener
和removeEventListener
方法。ListenerType2
使用on
和off
方法。
export type UseEventTarget = ListenerType1 | ListenerType2;
UseEventTarget
: 联合类型,表示可以是ListenerType1
或ListenerType2
中的任何一个。
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;
};
isListenerType1
和isListenerType2
: 类型谓词函数,用于检查target
是否符合ListenerType1
或ListenerType2
类型。
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
: 事件目标,可以是null
、ListenerType1
或ListenerType2
类型的对象,也可以是window
。options
: 事件选项,可能包括{ capture: boolean }
等。
-
useEffect
: 用于处理副作用,添加和移除事件监听器。依赖数组包含name
、handler
、target
和options
(序列化为字符串,以防options
是一个对象)。-
添加事件监听器:
isListenerType1(target)
: 如果target
是ListenerType1
类型,使用on
函数。isListenerType2(target)
: 如果target
是ListenerType2
类型,使用target.on
方法。
-
移除事件监听器:
- 在副作用清理函数中,使用
off
或target.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;
}
-
const mountedRef = useRef<boolean>(false);
- 使用
useRef
创建一个可变的引用mountedRef
,其初始值为false
。这个引用对象会在整个组件生命周期内保持不变。 mountedRef.current
用于存储组件的挂载状态。
- 使用
-
const get = useCallback(() => mountedRef.current, []);
- 使用
useCallback
创建一个记忆化的回调函数get
,该函数返回mountedRef.current
的值。 - 空的依赖数组
[]
表示get
函数不会因组件重新渲染而重新创建。这样可以确保每次返回的get
函数引用相同,避免不必要的重新渲染。
- 使用
-
useEffect(() => { ... }, []);
useEffect
用于执行副作用,在组件挂载时执行并在组件卸载时清理。mountedRef.current = true;
:当组件挂载时,将mountedRef.current
设置为true
。return () => { mountedRef.current = false; };
:在组件卸载时,将mountedRef.current
设置为false
,以便清理。
-
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;
};
-
const refUnmounted = useRef(false);
- 使用
useRef
创建一个引用refUnmounted
,初始值为false
,用于跟踪组件的卸载状态。
- 使用
-
useEffectOnce(() => () => { refUnmounted.current = true; });
useEffectOnce
确保副作用只在组件挂载时执行一次,并在组件卸载时执行清理。- 当组件卸载时,将
refUnmounted.current
设置为true
。这样,后续的 Promise 处理可以检查组件是否已经卸载。
-
const wrapper = useMemo(() => { ... }, []);
useMemo
用于创建一个记忆化的race
函数,避免每次渲染时都重新创建。
-
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
处理错误,或者在控制台记录错误。
- 在
-
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);
}),
[]
);
};
-
const isMounted = useMountedState();
- 调用
useMountedState
Hook 获取isMounted
函数,这个函数用于检查组件是否仍然挂载。
- 调用
-
return useCallback((promise: Promise<any>) => { ... }, []);
- 使用
useCallback
创建一个记忆化的函数,只有在依赖项(在此情况下为空数组[]
)发生变化时才会重新创建。 useCallback
确保这个回调函数在每次组件渲染时保持稳定的引用,从而避免不必要的重新渲染。
- 使用
-
new Promise<any>((resolve, reject) => { ... })
- 返回一个新的 Promise,该 Promise 封装了传入的
promise
。 onValue
函数:在promise
成功时调用。如果组件仍然挂载(通过isMounted()
检查),则解析 Promise。onError
函数:在promise
失败时调用。如果组件仍然挂载,则拒绝 Promise;如果组件已经卸载,可以选择执行错误处理函数(但在这里,错误会被直接抛出到控制台)。
- 返回一个新的 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
- usePromise
功能: 封装 Promise,确保只在组件挂载时更新状态。适用场景: 需要确保 Promise 操作的回调仅在组件挂载时执行,适用于简单的 Promise 封装和状态更新。- 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:useEffectOnce
和 useUpdateEffect
。下面我们详细解析 useLogger
的实现和功能。
import useEffectOnce from './useEffectOnce';
import useUpdateEffect from './useUpdateEffect';
useEffectOnce
: 这个自定义 Hook 确保其副作用(如日志记录)只在组件挂载时执行一次,并在组件卸载时执行清理操作。useUpdateEffect
: 这个自定义 Hook 确保其副作用(如日志记录)只在组件更新时执行(即组件的props
或state
发生变化时)。
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 invoked
fnRef.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 beyond
return () => { // *OPTIONAL*
// do something on unmount
}
}) // you can include deps array if necessary
return <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);
};
-
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
,表示组件不再是第一次挂载。
-
useEffect(() => { if (!isFirstMount) { return effect(); } }, deps);
-
useEffect
: 在组件渲染后执行副作用。在这里,它的依赖项数组是deps
,只有当这些依赖项发生变化时,useEffect
才会重新执行。 -
if (!isFirstMount) { return effect(); }
: 这个条件检查组件是否为第一次挂载。如果不是第一次挂载(即isFirstMount
为false
),则执行传入的副作用函数effect
。如果是第一次挂载,什么都不做。
-
useUpdateEffect
主要用于以下场景:
- 在组件更新时执行副作用: 当你只希望在组件更新时运行某些逻辑,而不希望在组件首次挂载时运行这些逻辑时,
useUpdateEffect
是一个很好的选择。例如,你可能只希望在数据更新时执行某些操作,而不是在组件第一次加载时执行这些操作。