遵义祥云平台网站建设,网站开发公司erp,电商网站建设注意事项,知名的网页制作公司多少钱【react.js hooks】useUrl 监听路由参数
本节我们来实现一个监听并解析 URL 参数的 hook#xff1a;useUrl。而且这个 hook 的返回类型是可推断的。
实现思路
监听 URL 变化 - 事件监听根据 URL 地址获取参数并返回 - 依赖工具方法推断参数结构 - 泛型参数#xff08;对象…【react.js hooks】useUrl 监听路由参数
本节我们来实现一个监听并解析 URL 参数的 hookuseUrl。而且这个 hook 的返回类型是可推断的。
实现思路
监听 URL 变化 - 事件监听根据 URL 地址获取参数并返回 - 依赖工具方法推断参数结构 - 泛型参数对象式 模板式返回参数 - 返回解析好的参数并合并 location 和 history 以提供更多功能
监听 URL
监听 popstate 事件即可注意因为是全局监听创建一个总事件。
// 全局的事件监听器
const listeners new SetFunction();window.addEventListener(popstate, () {// do somethinglisteners.forEach((listener) listener());
});解析参数
使用内置的 decodeURIComponent 解析参数即可
后面几个参数是对解析的细节配置 mode - 分了两种解析模式 string - 全解析为字符串auto - 智能解析 autoParams - 指定被智能解析的字段stringifyParams - 指定被解析为字符串的字段custom - 自定义参数解析的映射配置
function getParamsT(url: string,mode: string | auto auto,autoParams: (keyof T | (string {}))[] [],stringifyParams: (keyof T | (string {}))[] [],custom: { [K in keyof T]?: (value: string | undefined) any } {}
) {const params: {[key: string]: string | number | boolean | null | undefined;} {};// 先处理 custom 对象for (const key in custom) {const value new URLSearchParams(url).get(key);params[key] custom[key as keyof T]?.(value ?? undefined);}const questionMarkIndex url.indexOf(?);if (questionMarkIndex ! -1) {const queryString url.substring(questionMarkIndex 1);const pairs queryString.split();for (const pair of pairs) {const [key, value] pair.split();try {const decodedKey decodeURIComponent(key);const decodedValue decodeURIComponent(value);if (custom[decodedKey as keyof T]) {continue; // 如果这个键在 custom 对象中我们已经处理过它了}if (stringifyParams.includes(decodedKey)) {params[decodedKey] decodedValue;} else if (autoParams.includes(decodedKey) || mode auto) {if (decodedValue true) {params[decodedKey] true;} else if (decodedValue false) {params[decodedKey] false;} else if (decodedValue null) {params[decodedKey] null;} else if (decodedValue undefined) {params[decodedKey] undefined;} else if (!isNaN(Number(decodedValue))) {params[decodedKey] Number(decodedValue);} else {params[decodedKey] decodedValue;}} else {params[decodedKey] decodedValue;}} catch (error) {console.error(Failed to decode URL parameter:, error);}}}return params as T;
}类型推断
繁琐的类型体操Github ts 练习中的 ParseQueryString 魔改升级版加入了一些解析配置的泛型参数以支持尽可能细致的类型推断看看就好工作中不建议写费时间且头大虽然写完后用着很舒服…代码见完整实现。
完整实现
import { useState, useEffect, useMemo } from react;
import { ApplyMode, ParseQueryString, Prettify } from ./types;type UrlInfoT extends Recordstring, any {readonly params: PrettifyReadonlyT;readonly name?: string;
} Location History;type UrlChangeCallbackT extends Recordstring, any (urlInfo: UrlInfoT
) void;function getParamsT(url: string,mode: string | auto auto,autoParams: (keyof T | (string {}))[] [],stringifyParams: (keyof T | (string {}))[] [],custom: { [K in keyof T]?: (value: string | undefined) any } {}
) {const params: {[key: string]: string | number | boolean | null | undefined;} {};// 先处理 custom 对象for (const key in custom) {const value new URLSearchParams(url).get(key);params[key] custom[key as keyof T]?.(value ?? undefined);}const questionMarkIndex url.indexOf(?);if (questionMarkIndex ! -1) {const queryString url.substring(questionMarkIndex 1);const pairs queryString.split();for (const pair of pairs) {const [key, value] pair.split();try {const decodedKey decodeURIComponent(key);const decodedValue decodeURIComponent(value);if (custom[decodedKey as keyof T]) {continue; // 如果这个键在 custom 对象中我们已经处理过它了}if (stringifyParams.includes(decodedKey)) {params[decodedKey] decodedValue;} else if (autoParams.includes(decodedKey) || mode auto) {if (decodedValue true) {params[decodedKey] true;} else if (decodedValue false) {params[decodedKey] false;} else if (decodedValue null) {params[decodedKey] null;} else if (decodedValue undefined) {params[decodedKey] undefined;} else if (!isNaN(Number(decodedValue))) {params[decodedKey] Number(decodedValue);} else {params[decodedKey] decodedValue;}} else {params[decodedKey] decodedValue;}} catch (error) {console.error(Failed to decode URL parameter:, error);}}}return params as T;
}// 全局的事件监听器
const listeners new SetFunction();window.addEventListener(popstate, () {listeners.forEach((listener) listener());
});/*** ## useUrl hook* Converts a string to a query parameter object. Return an object merged with location, history, params and name.** ### Parameters* - callback (?) - The **callback** to call when the url changes.* - name (?) - The name of the listener* - immediate (false) - Whether to call the callback immediately.* - config (?) - The configuration of the params parser.* mode (auto) - The mode of the params parser: string | auto auto.* autoParams (?) - The parameters to treat as auto.* stringifyParams (?) - The parameters to treat as string.* custom (?) - The custom parser of certain query parameters.** ### Type Parameters* - T - string or object.* The string to convert, like http://localhost?id1nameevan* object: object to inferred as, like { id: 1, name: evan }* - Mode - The mode to use when converting: string | fuzzy | auto | strict | any auto.* - StrictParams - The parameters to treat as strict.* - FuzzyParams - The parameters to treat as fuzzy.** ### Notes* - Type infer mode is not associated with the mode parameter of parser.** return location merged with history, params and name.*/
function useUrlT extends Recordstring, any | string,Mode extends any | fuzzy | auto | auto | strict auto,StrictParams extends string[] [],FuzzyParams extends string[] []
(callback?: UrlChangeCallbackPartialT extends string? ParseQueryStringT, Mode, StrictParams, FuzzyParams: ApplyModeT, Mode, StrictParams, FuzzyParams,name?: string,immediate?: boolean,config: {mode?: string | auto;autoParams?: (| keyof (T extends string ? ParseQueryStringT : ApplyModeT)| (string {}))[];stringifyParams?: (| keyof (T extends string ? ParseQueryStringT : ApplyModeT)| (string {}))[];custom?: {[K in keyof (T extends string ? ParseQueryStringT : ApplyModeT)]?: (value: string | undefined) any;};} {}
): UrlInfoPartialT extends string? ParseQueryStringT, Mode, StrictParams, FuzzyParams: ApplyModeT, Mode, StrictParams, FuzzyParams{function getUrlInfo() {return {params: getParams(window.location.href,config?.mode,config?.autoParams,config?.stringifyParams,config?.custom),name: name,...window.location,...window.history,};}const [urlInfo, setUrlInfo] useStateUrlInfoT extends string? ParseQueryStringT, Mode, StrictParams, FuzzyParams: ApplyModeT, Mode, StrictParams, FuzzyParams(getUrlInfo() as any);const memoizedConfig useMemo(() config,[config.mode, config.autoParams, config.stringifyParams, config.custom]);useEffect(() {if (immediate) {const urlInfo getUrlInfo();callback?.(urlInfo as any);setUrlInfo(urlInfo as any);}}, [immediate, JSON.stringify(memoizedConfig), name]);useEffect(() {const handlePopState () {const urlInfo getUrlInfo();setUrlInfo(urlInfo as any);callback?.(urlInfo as any);};// 在组件挂载时注册回调函数listeners.add(handlePopState);return () {// 在组件卸载时注销回调函数listeners.delete(handlePopState);};}, [callback]);return urlInfo as any;
}export default useUrl;types:
/*** Converts a string to a query parameter object.* ### Parameters* - S - The string to convert, like http://localhost?id1nameevan.* - Mode - The mode to use when converting: string | fuzzy | auto | strict | any auto.** - StrictParams - The parameters to treat as strict.** - FuzzyParams - The parameters to treat as fuzzy.** return A query parameter object*/
export type ParseQueryStringS extends string,Mode extends string | fuzzy | auto | strict | any auto,StrictParams extends string[] [],FuzzyParams extends string[] []PrettifyS extends ${infer _Prefix}?${infer Params}? Params extends ? {}: MergeParamsSplitParamsParams, Mode, StrictParams, FuzzyParams: MergeParamsSplitParamsS, Mode, StrictParams, FuzzyParams
;type SplitParamsS extends string S extends ${infer E}${infer Rest}? [E, ...SplitParamsRest]: [S];type MergeParamsT extends string[],Mode extends string | fuzzy | auto | strict | any auto,StrictParams extends string[] [],FuzzyParams extends string[] [],M {}T extends [infer E, ...infer Rest extends string[]]? E extends ${infer K}${infer V}? MergeParamsRest,Mode,StrictParams,FuzzyParams,SetPropertyM, K, V, Mode, StrictParams, FuzzyParams: E extends ${infer K}? MergeParamsRest,Mode,StrictParams,FuzzyParams,SetPropertyM, K, undefined, Mode, StrictParams, FuzzyParams: never: M;type SetPropertyT,K extends PropertyKey,V extends any true,Mode extends string | fuzzy | auto | strict | any auto,StrictParams extends string[] [],FuzzyParams extends string[] []{[P in keyof T | K]: P extends K? P extends keyof T? T[P] extends V? T[P]: T[P] extends any[]? V extends T[P][number]? T[P]: [...T[P], V]: [T[P], V]: P extends FuzzyParams[number]? string: P extends StrictParams[number]? V extends true? true: V extends false? false: V extends null? null: V extends ${number}? number: V: Mode extends string? string: Mode extends fuzzy? string: Mode extends auto? V extends true | false? boolean: V extends null? null: V extends ${number}? number: string: Mode extends strict? V extends true? true: V extends false? false: V extends null? null: V extends ${number}? ToNumberV: V: Mode extends any? any: never: P extends keyof T? T[P]: never;
};export type ApplyModeT,Mode extends string | fuzzy | auto | strict | any auto,StrictParams extends string[] [],FuzzyParams extends string[] []Mode extends auto? T: {[P in keyof T]: P extends FuzzyParams[number]? string: P extends StrictParams[number]? T[P] extends true? true: T[P] extends false? false: T[P] extends null? null: T[P] extends ${number}? ToNumberT[P]: T[P]: Mode extends string? string: Mode extends fuzzy? string: Mode extends strict? T[P] extends true? true: T[P] extends false? false: T[P] extends null? null: T[P] extends ${number}? ToNumberT[P]: T[P]: Mode extends any? any: T[P];};export type PrettifyT {[K in keyof T]: T[K];
} {};使用示例
比如在地址栏中传 id 和 source 两个参数并更改它们的值
const { params } useUrl?id2sourceHangzhou((urlInfo) {console.log(id: ${urlInfo.params.id} source: ${urlInfo.params.source});},ursUrl exmaple listener,true // call immediately
);Bingo! 一个监听 URL 的 hook 就酱紫实现了TS 虽好但请慎用