构建 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( key: string, defaultValue: T ): [T, (newValue: Partial | T) => void] { const [searchParams, setSearchParams] = useSearchParams(); const paramValue = searchParams.get(key); const [state, setState] = useState (() => { if (paramValue === null) { return defaultValue; } try { return JSON.parse(paramValue) as T; } catch { return paramValue as T; } }); const setParamState = useCallback( (newValue: Partial | 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 中是否存在指定的搜索参数。如果是,钩子会解析它并将其用作初始状态。否则,它将回退到提供的defaultValue。
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('filter', { status: 'all', sortBy: 'date', }); return ( ); }; export default MyComponent;Current Filter: {filter.status}, Sort by: {filter.sortBy}
为了确保 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 }) => ({children} ); const { result } = renderHook(() => useParamState('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
免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。
Copyright© 2022 湘ICP备2022001581号-3