在 React 中,我们经常需要获取和处理 URL 中的查询参数。特别是在单页应用(SPA)中,路由参数的管理和转换非常常见。为了提高代码的复用性和可维护性,我们可以使用 React 的 Hook 来封装这类逻辑,确保代码简洁、易于维护。通过实现一个自定义的 useRouterParams Hook 来演示如何获取 URL 中的查询参数,并根据需求进行解析和转换。

背景

在前端开发中,路由查询参数(Query Parameters)是非常常见的。例如,在一个电商平台中,可能会通过 URL 的查询参数来传递过滤条件(如类别、价格区间等)。对于多页面应用(MPA)或单页面应用(SPA),获取和处理这些参数是必不可少的操作。

React 官方提供的 useLocation 和 useHistory 钩子可以帮助我们访问路由,但它们默认只返回完整的 URL 信息,没有直接提供对查询参数的处理。因此,我们需要编写一个自定义 Hook 来简化这个过程。

实现思路

我们希望创建一个 useRouterParams 的 Hook,它具有以下功能:
• 获取当前页面的查询参数。
• 可以选择对查询参数进行转换(例如,转换为数值类型或枚举类型)。
• 提供一种简便的方式来访问和管理查询参数。

代码实现

下面是 useRouterParams Hook 的实现代码:

import { useMemo } from 'react'

export type DefaultParams = Partial<Record<string, string>>

/**
 * 获取路由参数。
 * 这个钩子允许开发者类型化路由参数,并可选地提供一个解析函数来处理参数。
 *
 * @param option.parseFn 解析函数。默认路由参数都是string类型。有些时候你需要转换成其他类型(数值/枚举)等。你可以通过传递这个参数来处理参数。
 *
 */
export function useRouterParams<T = DefaultParams>(
  option: {
    transform?: (data: T) => T
  } = {}
) {
  const url = new URL(window.location.href)

  const params = getQueryParams(url?.href) as T

  const _param = useMemo(() => {
    // 计算key
    let cparam: T = params as T

    try {
      if (option.transform) cparam = option.transform(cparam)
    } catch (event) {
      // eslint-disable-next-line no-console
      console.error('parseFn 执行失败:', event)
    }

    return { ...params, ...cparam } as T
  }, [params])

  return [_param as T] as const
}

// 正则表达式提取查询字符串
function getQueryParams(url: string): Record<string, string> {
  const queryString = url.split('?')[1] || ''
  const params: Record<string, string> = {}

  // 匹配所有的 key=value 对
  queryString.replace(/([^=&]+)=([^&]*)/g, (_match, key, value) => {
    params[decodeURIComponent(key)] = decodeURIComponent(value)
    return ''
  })

  return params
}

简单实例