构建 React 应用程序时,在 URL 中反映状态通常很有用。这不仅使状态可共享,还允许用户在不丢失上下文的情况下添加书签或刷新页面。在这篇文章中,我们将在 TypeScript 中创建一个名为 useParamState 的自定义 React 钩子。这个钩子的功能类似于 useState,但它也会将状态与 URL 中的搜索参数同步。重要的是,它将支持复杂的对象值。
React Router 的 useSearchParams 钩子非常适合管理 URL 搜索参数,但将它们与组件状态同步可能很麻烦。 useParamState 钩子通过以下方式解决这个问题:
(这假设你已经知道如何建立一个 React 项目,如果不知道如何去 Vite)
确保你已经安装了react-router-dom:
npm install react-router-dom
以下是实现 useParamState 挂钩的方法:
import { useCallback, useState } from 'react'; import { useSearchParams } from 'react-router-dom'; /** * A custom hook that syncs state with a URL search parameter. * Supports string, number, boolean, and object values. * @param key The search parameter key to sync with. * @param defaultValue The default value for the state. * @returns A stateful value, and a function to update it. */ function useParamState<T extends string | number | boolean | object>( key: string, defaultValue: T ): [T, (newValue: Partial<T> | T) => void] { const [searchParams, setSearchParams] = useSearchParams(); const paramValue = searchParams.get(key); const [state, setState] = useState<T>(() => { if (paramValue === null) { return defaultValue; } try { return JSON.parse(paramValue) as T; } catch { return paramValue as T; } }); const setParamState = useCallback( (newValue: Partial<T> | T) => { const updatedValue = typeof newValue === 'object' && !Array.isArray(newValue) ? { ...state, ...newValue } : newValue; setState(updatedValue as T); const newSearchParams = new URLSearchParams(searchParams); newSearchParams.set(key, JSON.stringify(updatedValue)); setSearchParams(newSearchParams); }, [key, searchParams, setSearchParams, state] ); return [state, setParamState]; } export default useParamState;
钩子首先检查 URL 中是否存在指定的搜索参数。如果是,钩子会解析它并将其用作初始状态。否则,它将回退到提供的默认值。
setParamState 函数会更新内部状态和 URL 中的搜索参数。它使用 JSON.stringify 来序列化状态,允许我们在 URL 中存储复杂的对象。
该钩子通过利用 TypeScript 的泛型和 JSON 解析来支持各种类型(字符串、数字、布尔值和对象)。
让我们看看如何在 React 组件中使用 useParamState:
import React from 'react'; import useParamState from './useParamState'; interface FilterState { status: string; sortBy: string; } const MyComponent: React.FC = () => { const [filter, setFilter] = useParamState<FilterState>('filter', { status: 'all', sortBy: 'date', }); return ( <div> <h2>Current Filter: {filter.status}, Sort by: {filter.sortBy}</h2> <button onClick={() => setFilter({ status: 'active', sortBy: filter.sortBy })}> Active </button> <button onClick={() => setFilter({ status: 'completed', sortBy: filter.sortBy })}> Completed </button> <button onClick={() => setFilter({ ...filter, sortBy: 'priority' })}> Sort by Priority </button> </div> ); }; export default MyComponent;
为了确保 useParamState 钩子按预期工作,您可以使用 @testing-library/react 编写单元测试:
import { renderHook, act } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; import useParamState from './useParamState'; interface FilterState { status: string; sortBy: string; } test('should sync object state with search params', () => { const wrapper = ({ children }: { children: React.ReactNode }) => ( <MemoryRouter initialEntries={['/']}>{children}</MemoryRouter> ); const { result } = renderHook(() => useParamState<FilterState>('filter', { status: 'all', sortBy: 'date' }), { wrapper }); // Initial state expect(result.current[0]).toEqual({ status: 'all', sortBy: 'date' }); // Update state and URL act(() => { result.current[1]({ status: 'active', sortBy: 'priority' }); }); // Updated state expect(result.current[0]).toEqual({ status: 'active', sortBy: 'priority' }); });
useParamState 挂钩简化了状态与 URL 搜索参数同步的过程,使您的 React 应用程序更加健壮且用户友好。由于支持对象等复杂类型,此钩子是一个强大的工具,用于管理需要在页面重新加载时保留或通过 URL 共享的状态。
您可以进一步扩展此钩子以处理更复杂的数据结构,但对于大多数用例,此实现将满足您的需求。
(请对文章发表评论,以便我可以做得更好并改进我可能犯的任何错误,提前感谢。)
也欢迎在其他平台关注我
Github
以上是自定义 React hook 以将状态与 URL 同步的详细内容。更多信息请关注PHP中文网其他相关文章!