业务背景
B 端后台系统中常见的业务场景就是表单列表页面,当用户A浏览第三页表单时,通过 url 分享给用户B后希望B用户打开链接后页面和用户A看到的数据一致。这就需要将页面url参数解析并赋值到组件内状态,并且组件内部的状态更改后同步到url中(以便二次分享)。
解决方案
使用浏览器的原生 window.location 和 URLSearchParams 来实现这个功能。以下是一个示例,展示如何手动控制 URL 和组件状态的双向绑定,以实现分页功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
   | import React, { useState, useEffect } from 'react';
  function useQuery() {   return new URLSearchParams(window.location.search); }
  function MyComponent() {   const query = useQuery();
    const [pageNum, setPageNum] = useState(Number(query.get('pageNum')) || 1);   const [pageSize, setPageSize] = useState(Number(query.get('pageSize')) || 10);
    useEffect(() => {     const params = new URLSearchParams(window.location.search);     if (pageNum) {       params.set('pageNum', pageNum);     }     if (pageSize) {       params.set('pageSize', pageSize);     }     window.history.replaceState(null, '', '?' + params.toString());   }, [pageNum, pageSize]);
    const handlePageChange = (newPageNum) => {     setPageNum(newPageNum);   };
    const handlePageSizeChange = (newPageSize) => {     setPageSize(newPageSize);   };
    return (     <div>       <h1>分页组件</h1>       <p>Page Number: {pageNum}</p>       <p>Page Size: {pageSize}</p>       <button onClick={() => handlePageChange(pageNum + 1)}>Next Page</button>       <button onClick={() => handlePageSizeChange(20)}>Set Page Size to 20</button>     </div>   ); }
  export default MyComponent;
  | 
 
通过上面这种方式,可以实现 URL 参数和组件状态的双向绑定,使得分页信息可以在 URL 和组件状态之间保持同步。
如果每个页面都这样手控控制就会造成大量冗余代码,可以抽离成通用 hook, 这样其他组件使用时可以直接命名参数名、参数值。
抽离通用 hook:useSyncedQueryParam
以下是实现方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
   | import { useState, useEffect } from 'react';
  function useQueryParams() {   return new URLSearchParams(window.location.search); }
  function useSyncedQueryParam(paramName, defaultValue) {   const query = useQueryParams();   const [value, setValue] = useState(query.get(paramName) || defaultValue);
    useEffect(() => {     const params = new URLSearchParams(window.location.search);     if (value !== undefined && value !== null) {       params.set(paramName, value);     } else {       params.delete(paramName);     }     window.history.replaceState(null, '', '?' + params.toString());   }, [value, paramName]);
    return [value, setValue]; }
  export default useSyncedQueryParam;
  | 
 
可以在组件中使用这个通用 Hook,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
   | import React from 'react'; import useSyncedQueryParam from './useSyncedQueryParam';
  function MyComponent() {   const [pageNum, setPageNum] = useSyncedQueryParam('pageNum', 1);   const [pageSize, setPageSize] = useSyncedQueryParam('pageSize', 10);
    const handlePageChange = (newPageNum) => {     setPageNum(newPageNum);   };
    const handlePageSizeChange = (newPageSize) => {     setPageSize(newPageSize);   };
    return (     <div>       <h1>分页组件</h1>       <p>Page Number: {pageNum}</p>       <p>Page Size: {pageSize}</p>       <button onClick={() => handlePageChange(pageNum + 1)}>Next Page</button>       <button onClick={() => handlePageSizeChange(20)}>Set Page Size to 20</button>     </div>   ); }
  export default MyComponent;
   | 
 
这样就可以在多个组件中复用 useSyncedQueryParam Hook,轻松实现 URL 参数和组件状态的双向绑定。