🥂React常用的自定义Hooks🥂
今天要为大家分享一些React常用的自定义Hooks,帮助大家更好地利用Hooks来提升React组件的复用性和可维护性。让我们一起来了解一下吧!🌱
useTimeout
useTimeout
允许你在函数组件中创建一个定时器,以便在一定的延迟之后执行某个操作。
import { useEffect, useRef } from 'react';
/**
* 自定义 Hook:在指定延迟后执行回调函数
* @param callback 要执行的回调函数
* @param delay 延迟的毫秒数
*/
const useTimeout = (callback: () => void, delay: number): void => {
const savedCallback = useRef(() => {});
// 当传入的回调函数发生变化时,更新保存的回调函数
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
// 设置定时器,在延迟结束后执行回调函数
useEffect(() => {
const timer = setTimeout(() => {
savedCallback.current && savedCallback.current();
}, delay);
// 清除定时器
return () => clearTimeout(timer);
}, [delay]);
};
export default useTimeout;
在组件中使用 useTimeout
Hook:
import useTimeout from './useTimeout';
const MyComponent = () => {
useTimeout(() => {
console.log('Delayed action executed!');
}, 3000); // 3 秒延迟
return (
<div>
<p>MyComponent</p>
</div>
);
};
export default MyComponent;
在这个例子中,useTimeout
Hook 将会在组件渲染后的 3 秒钟后执行传入的回调函数,这里是 console.log('Delayed action executed!')
。
useInterval
useInterval
类似于useTimeout
,但是可以设置周期性地调用回调函数。
import { useEffect, useRef } from 'react';
/**
* 自定义 Hook:周期性地调用回调函数
* @param callback 要执行的回调函数
* @param delay 每次调用之间的间隔(毫秒)
*/
const useInterval = (callback: () => void, delay: number): void => {
const savedCallback = useRef(() => {});
// 当传入的回调函数发生变化时,更新保存的回调函数
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
// 设置周期性定时器,在每个间隔结束时调用回调函数
useEffect(() => {
const interval = setInterval(() => {
savedCallback.current && savedCallback.current();
}, delay);
// 清除定时器
return () => clearInterval(interval);
}, [delay]);
};
export default useInterval;
这个 useInterval
Hook 使用了两个 useEffect
钩子,第一个用来更新保存的回调函数,第二个用来设置周期性的定时器。在每个间隔结束时,定时器会调用保存的回调函数。
您可以像使用 useTimeout
一样使用 useInterval
,只需传入周期性地执行的回调函数和间隔时间即可。
以下是使用 TypeScript 编写的示例组件,演示如何使用 useInterval
Hook:
import { useState } from 'react';
import useInterval from './useInterval';
const Counter = () => {
const [count, setCount] = useState(0);
// 定义周期性的回调函数,每秒增加计数器的值
const incrementCount = () => {
setCount(prevCount => prevCount + 1);
};
// 使用 useInterval Hook,在每秒钟调用一次 incrementCount 回调函数
useInterval(incrementCount, 1000);
return (
<div>
<p>Count: {count}</p>
</div>
);
};
export default Counter;
在这个示例中,我们创建了一个计数器组件 Counter
,它使用了 useState
来跟踪计数器的值,并使用了 useInterval
Hook 来每秒钟增加计数器的值。通过这种方式,我们可以创建周期性执行的效果,而不必手动管理 setInterval
和 clearInterval
。
useTimes
useTimes
用来获取本地时间,并支持格式化,使用day.js格式化时间。
import { useEffect, useRef, useState } from 'react';
import dayjs from 'dayjs';
/**
* 自定义 Hook:周期性执行回调函数,并支持使用 day.js 格式化本地时间
* @param formatTime 格式化时间的函数,默认为 'YYYY-MM-DD HH:mm:ss'
* @param interval 每次执行之间的间隔(毫秒),默认为1000毫秒
*/
const useTimes = (formatTime: string = 'YYYY-MM-DD HH:mm:ss', interval: number = 1000): any => {
const timer:any = useRef(null);
const [time, setTime] = useState<string>(dayjs().format(formatTime));
useEffect(() => {
timer.current = setInterval(() => {
setTime(dayjs().format(formatTime));
}, interval);
// 在组件卸载时清除定时器
return () => {
if (timer.current) {
clearInterval(timer.current);
}
};
}, [formatTime, interval]);
return {time}
};
export default useTimes;
如果你需要在组件中使用这个 Hook,可以这样做:
import useTimes from './useTimes';
const MyComponent = () => {
const { time } = useTimes(); // 获取格式化后的时间
return (
<div>
<p>当前时间: {time}</p>
</div>
);
}
export default MyComponent;
这样你就可以在组件中实时显示当前格式化后的时间了。
useMount
useMount
是一个常见的自定义 Hook,用于在组件挂载时执行一次性操作。你可以像这样实现它:
import { useEffect, useRef } from "react";
/**
* 自定义 Hook:在组件挂载时执行一次性操作,支持处理异步操作
* @param fn 要执行的回调函数
*/
const useMount = (fn: () => Promise<void> | void): void => {
const callbackRender = useRef<Promise<void> | (() => void)>(() => {});
useEffect(() => {
callbackRender.current = fn();
return () => {
if (typeof callbackRender.current === "function") {
callbackRender.current();
} else if (Object.prototype.toString.call(callbackRender.current) === "[object Promise]") {
(callbackRender.current as Promise<() => void>).then((callback) => {
if (typeof callback === 'function') {
callback();
}
});
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); // 依赖项为空数组,确保回调只在组件挂载时执行
};
export default useMount;
然后你可以在组件中使用 useMount
Hook,传入需要执行的一次性操作:
import useMount from './useMount';
function MyComponent() {
useMount(() => {
console.log('组件挂载时执行的操作');
// 在这里执行你想要在组件挂载时执行的一次性操作
});
return <div>My Component</div>;
}
export default MyComponent;
这样,你就可以确保在组件挂载时执行一些初始化操作。
usePrevious
usePrevious
用于在组件更新时获取前一个状态或属性的值。
import { useEffect, useRef } from 'react';
/**
* 自定义 Hook:在组件更新时获取前一个状态或属性的值
* @param value 当前的状态或属性值
*/
const usePrevious = <T>(value: T): T | undefined => {
const ref = useRef<T>();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
};
export default usePrevious;
这个 usePrevious
Hook 接受一个泛型参数 T
,用于表示状态或属性的类型。它利用了 useRef
来在组件的多次渲染之间保持状态,并在每次渲染时更新 ref.current
的值为当前的状态或属性值。因此,通过在组件更新时返回 ref.current
的值,就可以获取到前一个状态或属性的值。
使用示例:
import { useState, useEffect } from 'react';
import usePrevious from './usePrevious';
const MyComponent = () => {
const [count, setCount] = useState(0);
const prevCount = usePrevious(count);
useEffect(() => {
console.log('Previous count:', prevCount);
}, [count, prevCount]);
return (
<div>
<button onClick={() => setCount((prev) => prev + 1)}>Increment</button>
<p>Current count: {count}</p>
</div>
);
};
export default MyComponent;
在这个示例中,每次点击按钮增加计数器时,会打印出前一个计数器的值。usePrevious
Hook 使得我们可以方便地在组件更新时获取到之前的状态或属性值。
useCopy
useCopy
可以用于处理复制文本到剪贴板的功能。
/**
* 复制文本到剪贴板
* @param val 要复制的文本内容
*/
const useCopy = async (val: string) => {
try {
if (navigator.clipboard && navigator.permissions) {
await navigator.clipboard.writeText(val);
return; // 如果成功,直接返回
}
// 降级方案
const textArea = document.createElement('textarea');
textArea.value = val;
textArea.setAttribute('readonly', 'readonly');
textArea.style.position = 'absolute';
textArea.style.left = '-9999px';
textArea.style.display = 'none'; // 将其隐藏起来
document.body.appendChild(textArea);
textArea.select();
// 尝试执行复制操作
const success = document.execCommand('copy');
if (!success) {
throw new Error('无法复制文本');
}
// 清理
document.body.removeChild(textArea);
} catch (err) {
throw new Error('无法复制文本');
}
};
export default useCopy;
使用示例:
import useCopy from './useCopy';
const MyComponent = () => {
const copy = (text) => useCopy(text);
const handleCopy = () => {
copy('要复制的文本内容');
};
return (
<div>
<button onClick={handleCopy}>复制文本</button>
</div>
);
};
export default MyComponent;
useDobounce
useDebounce
用于延迟执行某个操作
用于限制函数的执行频率,确保在一段时间内只执行一次。与节流不同,它不是在固定的时间间隔内执行函数,而是在函数最后一次被调用后等待一段时间再执行。
import React, { useCallback, useEffect, useRef } from 'react';
interface Current {
fn: (...args: any[]) => void;
timer?: NodeJS.Timeout | null;
}
/**
* 自定义 Hook:节流函数
* @param fn 要节流的函数
* @param delay 节流延迟时间,默认为 500 毫秒
* @param dep 依赖项数组
* @returns 经过节流处理的函数
*/
export function useThrottle(fn: (...args: any[]) => void, delay: number = 500, dep: React.DependencyList = []) {
const { current } = useRef<Current>({ fn, timer: null });
// 更新最新的函数
useEffect(() => {
current.fn = fn;
}, [fn]);
// 返回经过节流处理的函数
return useCallback(
function throttledFn(...args: any[]) {
if (!current.timer) {
// 如果当前没有计时器在运行,则设置计时器,在指定延迟后执行传入的函数
current.timer = setTimeout(() => {
current.timer = null;
current.fn.call(this, ...args);
}, delay);
}
},
[delay, dep]
);
}
示例:
import React, { useState } from 'react';
import { useDebounce } from './useDebounce'; // 假设您已经导入了 useDebounce 自定义 Hook
const MyComponent = () => {
const [searchTerm, setSearchTerm] = useState<string>('');
const [searchResults, setSearchResults] = useState<string[]>([]);
// 使用 useDebounce 自定义 Hook,创建防抖处理后的搜索函数
const debouncedSearch = useDebounce((term: string) => {
// 在这里执行实际的搜索操作,例如发送网络请求等
// 这里仅为示例,模拟一个搜索结果
setSearchResults([
`Mock result 1 for "${term}"`,
`Mock result 2 for "${term}"`,
`Mock result 3 for "${term}"`,
]);
}, 500); // 如果不提供 delay 参数,则默认为 500 毫秒
// 处理搜索输入变化的函数
const handleSearchInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const term = event.target.value;
setSearchTerm(term);
// 调用防抖处理后的搜索函数
debouncedSearch(term);
};
return (
<div>
<input
type="text"
value={searchTerm}
onChange={handleSearchInputChange}
placeholder="Enter search term"
/>
<ul>
{searchResults.map((result, index) => (
<li key={index}>{result}</li>
))}
</ul>
</div>
);
};
export default MyComponent;
在这个示例中,当用户输入搜索词时,会调用 handleSearchInputChange
函数。该函数更新组件的 searchTerm
状态,并调用经过防抖处理后的搜索函数 debouncedSearch
。debouncedSearch
函数在 useDebounce
Hook 内部被创建,并使用了默认的防抖延迟时间(500 毫秒)。最后,搜索结果将显示在组件中。
useThrottle
useThrottle
用于实现节流功能。
节流是一种限制函数的调用频率的技术,在一定时间间隔内,只允许函数执行一次。
你的代码已经相当不错了,但我稍作调整来添加函数注释和示例:
import React, { useCallback, useEffect } from 'react';
interface ThrottleOptions {
delay?: number; // 节流延迟时间,默认为 500 毫秒
}
interface Current {
fn: (...args: any[]) => void;
timer?: NodeJS.Timeout | null;
}
/**
* 自定义 Hook:节流函数
* @param fn 要节流的函数
* @param delay 节流延迟时间,默认为 500 毫秒
* @param dep 依赖项数组
* @returns 经过节流处理的函数
*/
export function useThrottle(fn: (...args: any[]) => void, delay: number = 500, dep: React.DependencyList = []) {
const current = React.useRef<Current>({ fn, timer: null });
// 更新最新的函数
useEffect(() => {
current.current.fn = fn;
}, [fn]);
// 返回经过节流处理的函数
return useCallback(
function throttledFn(...args: any[]) {
if (!current.current.timer) {
// 如果当前没有计时器在运行,则设置计时器,在指定延迟后执行传入的函数
current.current.timer = setTimeout(() => {
current.current.timer = null;
}, delay);
current.current.fn.call(this, ...args);
}
},
[delay, dep]
);
}
下面是一个示例,演示了如何在 React 组件中使用 useThrottle
:
import { useState } from 'react';
import { useThrottle } from './useThrottle';
const App = () => {
const [count, setCount] = useState<number>(0);
// 定义节流处理的函数,每点击一次按钮增加计数
const throttledIncrement = useThrottle(() => {
setCount((prevCount) => prevCount + 1);
}, 1000); // 设置延迟为 1000 毫秒
return (
<div>
<p>Count: {count}</p>
<button onClick={throttledIncrement}>Increment</button>
</div>
);
};
export default App;
在这个示例中,每次点击按钮都会触发 throttledIncrement
函数,但由于使用了节流,所以在 1000 毫秒内多次点击只会执行一次增加计数的操作。
useQuery
useQuery
通常用于处理 URL 查询参数。
它可以帮助你轻松地从 URL 中获取查询参数的值,并在组件中使用这些值。
import { useLocation } from 'react-router-dom';
// 定义查询参数的类型
interface QueryParams {
[key: string]: string;
}
const useQuery = (): QueryParams => {
// 使用 useLocation 获取当前页面的 location 对象
const location = useLocation();
// 获取查询参数
const queryParams = new URLSearchParams(location.search);
const queryObj: QueryParams = {};
// 遍历 URL 查询字符串中的参数,并存储到 queryObj 中
queryParams.forEach((value, key) => {
queryObj[key] = value;
});
return queryObj; // 返回查询参数对象
};
export default useQuery;
使用示例:
import useQuery from './useQuery';
const MyComponent = () => {
// 使用 useQuery 获取查询参数
const queryParams = useQuery();
// 使用查询参数中的值
const name = queryParams.name || 'Guest';
const age = queryParams.age || 'Unknown';
return (
<div>
<h1>Hello, {name}!</h1>
<p>You are {age} years old.</p>
</div>
);
};
export default MyComponent;
在这个示例中,useQuery
自定义 Hook 会在组件挂载时获取当前页面的 URL 查询参数,并将其存储为一个对象。然后,我们可以在组件中使用这些查询参数,比如在标题和段落中显示用户的姓名和年龄。
useNetwork
useNetwork
用来监测用户的网络连接状态。
这个 Hook 通常会返回一个包含网络连接信息的对象,例如是否连接到互联网、当前网络类型等。
import { useState, useEffect } from 'react';
/**
* 自定义 Hook:用于检测用户的网络连接状态
* @returns {boolean} 当前网络连接状态,true 表示在线,false 表示离线
*/
const useNetwork = (): boolean => {
const [isOnline, setIsOnline] = useState<boolean>(navigator.onLine);
/**
* 更新网络连接状态
*/
const updateOnlineStatus = (): void => {
setIsOnline(navigator.onLine);
};
useEffect(() => {
// 添加 online 和 offline 事件监听器
window.addEventListener('online', updateOnlineStatus);
window.addEventListener('offline', updateOnlineStatus);
// 在组件卸载时移除事件监听器
return () => {
window.removeEventListener('online', updateOnlineStatus);
window.removeEventListener('offline', updateOnlineStatus);
};
}, []);
return isOnline;
};
export default useNetwork;
在这个示例中,我们利用了 useState
和 useEffect
来追踪用户的网络连接状态。当组件加载时,我们注册了 online
和 offline
事件的监听器来更新 isOnline
的状态。另外,我们还尝试使用 navigator.connection
来获取网络类型信息,并更新 networkType
的状态。
你可以在需要监测网络连接状态的组件中使用这个 useNetwork
自定义 Hook,从而实时获取用户的网络状态信息。
import useNetwork from './useNetwork'; // 你的自定义 Hook 文件路径
const NetworkStatusComponent = () => {
const isOnline = useNetwork();
return (
<div>
<p>Network Status: {isOnline ? 'Online' : 'Offline'}</p>
</div>
);
}
export default NetworkStatusComponent;
在这个示例中,NetworkStatusComponent
组件会根据 isOnline
变量的值显示当前的网络连接状态。当用户的网络连接状态发生变化时,useNetwork
Hook 会自动更新 isOnline
的值,并触发组件的重新渲染,从而实现了网络状态的实时展示。
useRisize
useResize
是一个自定义 Hook,用于监听 DOM 元素的大小变化。
它通常用于需要响应元素大小变化的场景,例如自适应布局或者调整组件尺寸等。
import { useState, useEffect } from 'react';
import type { RefObject } from 'react';
interface Dimensions {
width: number;
height: number;
}
/**
* 自定义 Hook:用于在组件中监测元素的尺寸变化
* @param ref RefObject<HTMLElement> - 对需要监测尺寸的元素的引用
* @returns {Dimensions} 元素的宽度和高度
*/
const useResize = (ref: RefObject<HTMLElement>): Dimensions => {
const [dimensions, setDimensions] = useState<Dimensions>({ width: 0, height: 0 });
useEffect(() => {
/**
* 更新元素尺寸
*/
const updateDimensions = (): void => {
if (ref.current) {
const { width, height } = ref.current.getBoundingClientRect();
setDimensions({ width, height });
}
};
// 获取初始尺寸
updateDimensions();
// 添加 resize 事件监听器
const handleResize = (): void => {
updateDimensions();
};
window.addEventListener('resize', handleResize);
// 在组件卸载时移除事件监听器
return () => {
window.removeEventListener('resize', handleResize);
};
}, [ref]);
return dimensions;
};
export default useResize;
在这个示例中,useResize
接受一个参数 ref
,这个 ref
是一个 React ref,用于指向需要监听大小变化的 DOM 元素。它使用了 useState
来追踪元素的宽度和高度。然后,利用 useEffect
在组件挂载和大小变化时更新元素的尺寸,并添加一个 resize
事件监听器来监听窗口大小变化。最后,返回元素的尺寸对象。
你可以在需要监听 DOM 元素大小变化的地方使用这个 useResize
自定义 Hook,来获取元素的实时尺寸信息。
下面是一个简单的示例,演示如何在 React 组件中使用 useResize
自定义 Hook:
import { useRef } from 'react';
import useResize from './useResize';
const ResizeComponent = () => {
const elementRef = useRef<HTMLDivElement>(null); // 创建一个 ref 来引用需要监听大小变化的 DOM 元素
const { width, height } = useResize(elementRef); // 使用 useResize 自定义 Hook
return (
<div ref={elementRef} style={{ width: '30%', height: '200px', backgroundColor: 'lightblue' }}>
<p>Width: {width}px</p>
<p>Height: {height}px</p>
</div>
);
};
export default ResizeComponent;
在这个示例中,我们首先导入了 useResize
自定义 Hook。然后,在组件中创建了一个 elementRef
来引用一个 div
元素,这个 div
元素的大小变化将被监听。接着,我们调用 useResize(elementRef)
来获取 width
和 height
,它们是从 useResize
Hook 返回的。
最后,我们在 JSX 中渲染了一个 div
元素,并将 elementRef
赋给它的 ref
属性,这样 useResize
就能够监听该 div
元素的大小变化。同时,我们在 div
元素内显示了当前的宽度和高度。
这样,当 div
元素的大小发生变化时,width
和 height
就会自动更新,并在 UI 中显示出来。
useClickInside
useClickInside
用于检测用户是否在指定的元素内点击。
它可以帮助您在用户点击元素内部时执行特定的操作。
好的,以下是一个使用 TypeScript 实现的 useClickInside
自定义 Hook,用于检测点击事件是否发生在指定的 DOM 元素内部:
import { useEffect } from 'react';
import type { RefObject } from 'react';
/**
* 自定义 Hook:用于检测点击事件是否发生在给定元素的内部
* @param ref RefObject<HTMLElement> - 要检测点击事件的元素的引用
* @param callback () => void - 点击事件发生在元素内部时要执行的回调函数
*/
const useClickInside = (ref: RefObject<HTMLElement>, callback: () => void) => {
useEffect(() => {
/**
* 处理点击事件
* @param event MouseEvent - 点击事件对象
*/
const handleClickInside = (event: MouseEvent) => {
if (ref.current && ref.current.contains(event.target as Node)) {
callback();
}
};
// 添加点击事件监听器
document.addEventListener('click', handleClickInside);
// 在组件卸载时移除点击事件监听器
return () => {
document.removeEventListener('click', handleClickInside);
};
}, [ref, callback]);
};
export default useClickInside;
这个 Hook 接受两个参数:ref
,用于引用需要监听点击事件的 DOM 元素,和 callback
,用于处理点击事件的回调函数。它使用了 useEffect
来在组件挂载和卸载时注册和取消事件监听。在事件处理函数中,我们检查点击事件的目标是否在指定的 DOM 元素内部,如果不在,则调用传入的 callback
处理函数。
以下是一个示例,演示了如何在 React 组件中使用 useClickInside
:
import { useRef } from 'react';
import useClickInside from './useClickInside';
const ClickInsideComponent = () => {
const containerRef = useRef<HTMLDivElement>(null);
const handleClickInside = () => {
alert('Clicked inside the container!');
};
useClickInside(containerRef, handleClickInside);
return (
<div ref={containerRef} style={{ width: '300px', height: '200px', backgroundColor: 'lightblue' }}>
<p>Click inside this container</p>
</div>
);
};
export default ClickInsideComponent;
在这个示例中,我们创建了一个 containerRef
来引用一个 div
元素,然后定义了一个 handleClickInside
函数来处理点击事件。接着,我们使用 useClickInside
自定义 Hook,并传入 containerRef
和 handleClickInside
。这样,当用户点击 div
元素内部时,会弹出一个提示框,说明点击事件发生在容器内部。
useClickOutside
useClickOutside
用来监听点击事件是否发生在指定的 DOM 元素外部,
你可以将 useClickInside
改名为 useClickOutside
,然后稍作修改以便监听点击事件是否发生在指定的 DOM 元素外部。
import { useEffect } from 'react';
import type { RefObject } from 'react';
/**
* 自定义 Hook:用于检测点击事件是否发生在给定元素的外部
* @param ref RefObject<HTMLElement> - 要检测点击事件的元素的引用
* @param callback () => void - 点击事件发生在元素外部时要执行的回调函数
*/
const useClickOutside = (ref: RefObject<HTMLElement>, callback: () => void) => {
useEffect(() => {
/**
* 处理点击事件
* @param event MouseEvent - 点击事件对象
*/
const handleClickOutside = (event: MouseEvent) => {
if (ref.current && !ref.current.contains(event.target as Node)) {
callback();
}
};
// 添加点击事件监听器
document.addEventListener('click', handleClickOutside);
// 在组件卸载时移除点击事件监听器
return () => {
document.removeEventListener('click', handleClickOutside);
};
}, [ref, callback]);
};
export default useClickOutside;
现在,这个 useClickOutside
自定义 Hook 将监听点击事件是否发生在指定的 DOM 元素外部,并在点击外部时调用回调函数。你可以将这个 Hook 应用到需要这种功能的组件中。
useFocus
useFocus
用于聚焦元素
import { useRef, useCallback } from "react";
import type { RefObject } from 'react';
/**
* 自定义 Hook,用于管理焦点状态
* @returns [ref: React.RefObject, focusElement: () => void]
*/
export const useFocus = (): any => {
const ref = useRef<HTMLElement>(null);
/**
* 将焦点设置到 ref 所指向的元素
*/
const focusElement = useCallback(() => {
if (ref.current) {
ref.current.focus();
}
}, []);
return [ref, focusElement];
};
假设你有一个输入框组件,你想要在某个条件满足时自动将焦点定位到输入框上。你可以使用 useFocus
自定义 Hook 来实现这个功能。
首先,让我们创建一个简单的输入框组件:
import { useEffect } from 'react';
import { useFocus } from './useFocus'; // 假设你将自定义 Hook 放在了 useFocus.ts 文件中
const InputComponent = () => {
const [inputRef, focusInput] = useFocus();
// 在组件挂载时自动将焦点定位到输入框上
useEffect(() => {
focusInput();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); // 依赖项为空数组,表示只在组件挂载时执行一次
return (
<input ref={inputRef} type="text" placeholder="输入框" />
);
};
export default InputComponent;
在这个示例中,我们使用了 useFocus
自定义 Hook 来获取一个 ref 和一个函数用于将焦点设置到该 ref 所指向的元素上。然后,在组件挂载时,我们调用 focusInput
函数,将焦点自动定位到输入框上。
你可以将这个 InputComponent
组件放入你的应用程序中的任何地方,并且会在挂载时自动将焦点定位到输入框上。
useMediaQuery
useMediaQuery
用于监听媒体查询的变化
import { useState, useEffect } from "react";
/**
* 自定义 Hook,用于监听媒体查询的变化
* @param query 媒体查询字符串,例如 '(min-width: 768px)'
* @returns 是否匹配给定媒体查询的布尔值
*/
export const useMediaQuery = (query: string): boolean => {
const [matches, setMatches] = useState<boolean>(false);
useEffect(() => {
const mediaQuery = window.matchMedia(query);
// 处理媒体查询变化的事件函数
const handleMediaQueryChange = (event: MediaQueryListEvent) => {
setMatches(event.matches);
};
// 添加事件监听器
mediaQuery.addEventListener("change", handleMediaQueryChange);
// 在组件挂载时检查媒体查询是否匹配
setMatches(mediaQuery.matches);
// 返回清除函数以在组件卸载时移除监听器
return () => {
mediaQuery.removeEventListener("change", handleMediaQueryChange);
};
}, [query]);
return matches;
};
假设我们有一个组件,根据屏幕宽度是否超过 768px 来显示不同的内容。我们可以使用 useMediaQuery
Hook 来监听屏幕宽度的变化,并根据匹配结果来决定要显示的内容。
import { useMediaQuery } from "./useMediaQuery";
const ExampleComponent = () => {
// 使用自定义 Hook 监听屏幕宽度是否超过 768px
const isLargeScreen = useMediaQuery("(min-width: 768px)");
return (
<div>
{isLargeScreen ? (
<p>屏幕宽度超过 768px,显示大屏幕内容。</p>
) : (
<p>屏幕宽度小于等于 768px,显示小屏幕内容。</p>
)}
</div>
);
};
export default ExampleComponent;
在这个示例中,我们导入了 useMediaQuery
自定义 Hook,并在组件中使用它来监听屏幕宽度是否超过 768px。然后根据匹配结果,决定显示不同的内容。
你可以将这个示例组件集成到你的 React 应用中,并根据需要调整媒体查询的条件和显示的内容。
useToggle
useToggle
可以帮助你在组件中轻松管理开关状态。
import { useState, useCallback } from "react";
/**
* 自定义 Hook,用于管理开关状态
* @param initialValue 开关的初始值,默认为 false
* @returns 包含开关状态和切换开关函数的对象
*/
const useToggle = (initialValue: boolean = false) => {
const [value, setValue] = useState<boolean>(initialValue);
// 切换开关状态的回调函数
const toggle = useCallback(() => {
setValue((currentValue) => !currentValue);
}, []);
return { value, toggle };
};
export default useToggle;
使用 useToggle
自定义 Hook 后,你可以在组件中轻松管理开关状态。下面是一个示例:
import useToggle from "./useToggle";
const ExampleComponent = () => {
// 使用自定义 Hook 管理开关状态
const { value, toggle } = useToggle();
return (
<div>
<button onClick={toggle}>
{value ? "关闭" : "打开"}开关
</button>
<p>开关状态:{value ? "开启" : "关闭"}</p>
</div>
);
};
export default ExampleComponent;
在这个示例中,我们使用 useToggle
自定义 Hook 来管理开关状态,并在组件中使用开关按钮来切换状态。当点击按钮时,开关状态会切换,并在页面上显示当前的状态。
useLocalStrage
useLocalStorage
可以帮助你在组件中轻松管理本地存储的值。
import { useState, useEffect } from "react";
import type { Dispatch, SetStateAction } from 'react';
// 定义泛型,用于指定存储值的类型
type SetValue<T> = Dispatch<SetStateAction<T>>;
// 自定义 Hook,用于在本地存储中管理值
const useLocalStorage = <T>(key: string, initialValue: T | (() => T)): [T, SetValue<T>] => {
// 使用 useState 的初始化函数来处理初始值
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key);
// 如果本地存储中存在对应的值,则解析并返回它
return item ? JSON.parse(item) :
// 否则,如果初始值是函数,则调用它
typeof initialValue === 'function' ? (initialValue as () => T)() : initialValue;
} catch (error) {
console.error(error);
// 如果出现错误,则返回初始值
return initialValue;
}
});
// 更新本地存储中的值,并更新状态
const setValue: SetValue<T> = (value) => {
try {
// 如果 value 是函数,则调用它以获取最新值
const valueToStore = value instanceof Function ? value(storedValue) : value;
// 更新状态
setStoredValue(valueToStore);
// 将值存储到本地存储中
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(error);
}
};
useEffect(() => {
const handleStorageChange = () => {
try {
// 从本地存储中获取最新的值
const item = window.localStorage.getItem(key);
// 如果存在,则更新状态
setStoredValue(item ? JSON.parse(item) :
// 如果不存在,则使用初始值
typeof initialValue === 'function' ? (initialValue as () => T)() : initialValue);
} catch (error) {
console.error(error);
}
};
// 监听本地存储变化
window.addEventListener("storage", handleStorageChange);
return () => {
// 清除事件监听器
window.removeEventListener("storage", handleStorageChange);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [key]); // 只有 key 变化时才重新订阅
return [storedValue, setValue];
};
export default useLocalStorage;
请看下面的示例,展示了如何使用这个 useLocalStorage
自定义 Hook:
import useLocalStorage from "./useLocalStorage";
const App = () => {
// 使用 useLocalStorage 自定义 Hook 来管理本地存储中的值
const [name, setName] = useLocalStorage<string>("name", "");
return (
<div>
<h1>Hello, {name || "Stranger"}!</h1>
<input
type="text"
placeholder="Enter your name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</div>
);
};
export default App;
在这个示例中,我们在组件中使用了 useLocalStorage
Hook 来管理名为 "name" 的本地存储中的值。通过 name
和 setName
变量,我们可以访问和更新本地存储中的值,并且在输入框中实时反映出来。
你可以根据需要修改键名和初始值,并将这个 Hook 应用到任何需要在本地存储中管理状态的组件中。
转载自:https://juejin.cn/post/7360486735799697444