Stories

Detail Return Return

ReactRouter6 的一些坑點 - Stories Detail

問題

這幾年忙着寫 Taro 相關業務,所以很久沒有再接觸 ReactRouter 了。從當年使用的 ReactRouter v3 & VueRouter v2,功能和寫法都沒什麼差別,而到現在的 ReactRouter v6,就感覺變化十分大。這裏從使用者的角度聊聊,初次上手 v6 的感受和如何應對這些變化。

變化

範式

函數化和標準化,讓其源碼減少了一半

  • v6 全面擁抱 Hooks,API 不再集中在一個對象上。同時這意味着如果你的項目還不支持 Hooks,那就應該使用更早前的版本。
  • 使用 URLSearchParams。它是標準化的 WebAPI,react-router-dom 庫目標是儘可能往標準化靠攏,少一些自定義對象和方法。

URL 參數獲取

  • useParams() 用於獲取路徑參數,比如 URL 格式是 '/user-info/:userId',就可以用 let { userId } = useParams() 獲得用户 id。
  • useSearchParams() 用於獲取 search 參數,比如常見的 '?name=foo&id=boo' ,就可以用 [searchParams, setSearchParams] = useSearchParams() 得到數據和數據修改方法

    • 從教程和 searchParams 的類型可知,searchParams 的類型是 URLSearchParams,不是普通對象。
    • 參數值要通過 searchParams.get('name') 的方式獲取。
    • 通過 setSearchParams() 可以即時改變當前 URL 的 search 部分

URL 參數設置

官方文檔的例子,只提供了手動拼接路徑參數或者 search 參數成 URL 的方式,比如

const navigate = useNavigate();
const url = 'foo/boo' + '?name=1&id=2';
navigate(url);

但是,我們希望在編寫業務邏輯的時候可以方便地傳入對象,比如 navigate('foo/boo', {name:1, id:2})。再根據上面提到的,範式上傾向於使用 URLSearchParams,所以我們封裝了以下方法

/**
 * @description: 路由參數設置到 url 上,得到新的 url
 * @param url 跳轉地址
 * @param params 路由參數(非必填,所有單據都是一個路由,只有參數不一樣,這個時候需要增加這個參數)
 * @return 組合出來的帶參數地址
 */
function setRouterParams(url: string, params?: Record<PropertyKey, any>): string {
  const searchParams = new URLSearchParams(params);
  return `${url}?${searchParams.toString()}`;
}

const url = setRouterParams('foo/boo', {name:1, id:2}); // 'foo/boo?name=1&id=2'
navigate(url);

History 對象

我們想要監聽 history 的變化,以動態改變頁面的標題。但是搜索發現,createBrowserHistory() 已經不在文檔內了。

從 issue 討論可知,v6.4 後 history 的主要功能已經合併到數據路由中(通過 createHashRouter(), createBrowserRouter() 之類生成的路由),比如想要實現監聽功能

const router = createHashRouter(routerList);

function App() {
  const [pageTitle, setPageTitle] = useState('');
  const { hash } = window.location;

  useEffect(() => {
    router.subscribe(({ location }) => {
      const { pathname } = location;
      const targetRoute = routerList.find((item) => item.path === pathname);
      setPageTitle(targetRoute?.title || ''); // 標題跟着變化
    });
  }, []);

  return (
    <div className='App'>
      {/* react-helmet 可以方便地修改 document.title */}
      <Helmet>
        <title>{pageTitle}</title>
      </Helmet>
      <RouterProvider router={router} />
    </div>
  );
}

更多特性變化未完待續...

總結

React 的相關插件都有函數化和標準化的趨勢。為了更好地適應技術更新,我們需要更新 WebAPI 知識,更熟練地掌握 Hooks 範式。

不過還是需要吐槽,Hooks 的初衷是,渲染是一種功能而已,沒必要把屬性都綁定在 state 裏,所以只需要把渲染作為函數引入即可。它的目的是不是“反 OOP”,只是 Hooks 範式更好。ReactRouter v6 把功能都拆散,業務邏輯的模塊化程度就降低了,無疑增加了學習和維護成本。

Add a new Comments

Some HTML is okay.