我今天很高兴与您分享一些先进的React Router 技术。 React Router 是一个令人惊叹的工具,它使我们能够在React应用程序中创建动态且流畅的导航。
虽然它通常用于满足基本的路由需求,但有一些鲜为人知的技术可以将您的路由能力提升到一个全新的水平。
今天,我将分享一些我多年来发现的最喜欢的东西。
useNavigate
和useLocation
挂钩。
当您读完本文时,您将深入掌握这些先进的 React Router 技术,并有信心将它们应用到您自己的项目中。
那么,让我们一起深入研究并提高我们的路由技能!
你准备好了吗?让我们开始吧!
在开展大型项目时,必须注意找不到的路线。我们经常将这些路由称为404 路由,因为当在服务器上找不到文档时,它们会返回HTTP 状态代码404。
顺便说一句,如果您是猫爱好者,您可能会喜欢查看http.cat 。
现在,让我们深入创建一个特殊的路由,只要没有其他路由与浏览器中的当前 URL 匹配,就会触发该路由。
import {BrowserRouter, Routes, Route} from "react-router-dom"; function App() { return <BrowserRouter> <Routes> <Route path="/" element={<p>Landing page</p>}></Route> <Route path="/products" element={<p>Products page</p>}></Route> <Route path="*" element={<p>404 page</p>}></Route> </Routes> </BrowserRouter> }
当访问主页/
时,用户将被引导至登陆页面。导航到/products
页面将引导他们进入“产品”页面。但是,如果访问任何其他不对应特定路由的链接,则会显示 404 页面。
此行为背后的原因是为 404 页面实现了专门的<Route>
,其中 path 属性设置为*
。此配置确保当没有其他路由可匹配时,React Router 将独占使用该路由。 404 路由的具体位置并不重要,可以作为其不可或缺的组成部分放置在<Routes>...</Routes>
部分内的任何位置。
应用程序可以实际使用路由的另一种方法是突出显示菜单中当前活动的页面。
主页 - 此链接将用户带到主页/
。
关于 - 此链接将用户带到关于页面/about
。
使用 React Router,当用户位于/about
路由时,可以自动突出显示About
链接。同样,当用户位于/
路线上时,您可以突出显示主页链接。最好的部分是,您无需复杂的条件即可实现此目的。
import {NavLink} from "react-router-dom"; function getClassName({isActive}) { if (isActive) { return "active"; // CSS class } } function App() { return <ul> <li> <NavLink to="/" className={getClassName}>Home</NavLink> </li> <li> <NavLink to="/about" className={getClassName}>About</NavLink> </li> </ul> }
您在上面看到的代码需要一些 CSS 才能使active
类正常工作。因此,我们可以通过使当前活动页面的菜单链接显示为粗体并突出来增强它。
.active { font-weight: bold; }
NavLink
元素的className
属性可以灵活地接受函数而不仅仅是字符串。当您决定使用一个函数时,它将获得一个对象作为参数,并且该对象将已经设置了isActive
属性。为了在函数中轻松访问此属性,我们通过在参数中包含{isActive}
来使用解构。
接下来,我们继续验证isActive
是否具有真值。如果是这种情况,我们将返回CSS类名active
。
当 React Router 中的当前路径与NavLink
组件的to
属性匹配时, isActive
函数将计算为布尔值。
因此,React Router 将无缝地将active
类包含在相应的<NavLink />
元素中,将其指示为当前活动的路由。
如果您喜欢更短的方法,可以使用三元运算符来重构上面提到的代码。该运算符提供了一种使用以下格式替换 if/else 语句的方法: condition ? truthy expression : falsy expression
。
我实际上不太使用三元运算符,因为它通常会降低代码的可读性。但是,在这种情况下使用是可以接受的。
function getClassName({isActive}) { if (isActive) { return "active"; } else { return ""; } }
function getClassName({isActive}) { return isActive ? "active" : ""; }
您只需确保 return 语句位于三元运算符之外。当isActive
条件为 true 时, ?
后面的表达式将被执行"active"
。否则, :
之后的表达式将被执行""
。
现在,我们可以消除对名为getClassName
的单独函数定义的需要。相反,我们可以使用像这样的箭头函数: ({isActive}) => isActive ? "active" : ""
。
如果您发现此更改太难以阅读,您可以像以前一样继续使用单独的函数。
import {NavLink} from "react-router-dom"; function App() { return <ul> <li> <NavLink to="/" className={({isActive}) => isActive ? "active" : ""}>Home</NavLink> </li> <li> <NavLink to="/about" className={({isActive}) => isActive ? "active" : ""}>About</NavLink> </li> </ul> }
哦,孩子,我几乎能听到你的想法!
Sojin,你已经违反了 DRY 原则,不是吗?
这只是一个例子。无需全力以赴 😀
您可能已经注意到,我使用的是NavLink
而不是我们常用的朋友Link
。但别担心,让我解释一下原因。您会看到, <Link />
组件不允许您使用className
属性的函数定义。是的,这可能会令人惊讶,但这就是您使用<Link />
时的情况。
我见过的一个常见错误是初级 React 开发人员尝试在<Link />
组件的className
属性中使用函数定义,但这根本不起作用。
如果你有兴趣,我可以写一篇关于这个主题的文章。请在评论中告诉我!
在某些情况下,您可能需要以编程方式重新定位 React 应用程序中的页面。例如,要将访问者重定向到特定页面,您可以提交请求并等待响应。
/dashboard
。
这需要使用useNavigate()
挂钩,它返回一个函数,您可以使用该函数以编程方式将用户重定向到新路线。
看一看:
import React, {useEffect} from "react"; import {useNavigate} from "react-router-dom"; function HelpPage() { const navigate = useNavigate(); useEffect(() => { const isLoggedIn = window.localStorage.getItem("isLoggedIn"); if (!isLoggedIn) { navigate("/login"); } }, []); return <h2>Help page</h2>; }
在此示例中,我们检查 localStorage 是否已分配给键isLoggedIn
的值。如果没有,我们将使用navigate("/login")
函数将用户重定向到/login
页面。
要使其正常工作,只需记住在嵌套在<BrowserRouter />
组件内的组件中使用useNavigate()
挂钩即可。否则,它将无法正常工作。
例如,以下示例将不起作用:
import React from "react"; import {BrowserRouter, useNavigate} from "react-router-dom"; function App() { // ❌ This breaks because the component was not wrapped by BrowserRouter, but its children are. const history = useNavigate(); return <BrowserRouter> {/* ... */} </BrowserRouter>; }
如果您尝试在App()
组件内使用useNavigate()
挂钩,您可能会遇到一些问题。这是因为除非您使用<BrowserRouter />
包装App()
组件,否则它将无法工作。
但别担心!
好消息是 App 内的所有子组件都会自动被<BrowserRouter />
包装,因此您可以愉快地在这些子组件中使用useNavigate()
而不会出现任何问题。
但是,如果您确实需要在<App />
组件本身中使用useNavigate()
,有一个简单的解决方案。您只需要创建一个包装<App />
的父组件,并确保将<BrowserRouter />
移动到该父组件内。完成此操作后,您就可以开始使用<App />
组件中的useNavigate()
来满足您的需求了 💗
当谈到使您的网站易于访问时,请务必注意链接的结构。为了确保这一点,建议使用<Link />
元素而不是其他方法。
为什么?
好吧,这些元素将自动呈现为<a>
标签,这些标签以其可访问性功能而闻名。
现在,这里有一个友好的提示:避免使用useNavigate()
提供的.push()
方法替换普通的<Link />
元素。仅当无法使用<Link />
元素时才应使用此方法。
通常,当您需要根据某些逻辑(例如fetch()
操作的结果)以编程方式重定向用户时。
如果你想每次 React Router 带你到一个新地址时执行一段代码怎么办?
您可以借助useLocation
挂钩轻松完成此任务。但在我们深入了解细节之前,让我们先探讨一些可以派上用场的场景。最常见的用例之一是将页面浏览事件发送到您的分析服务,例如 Google Analytics。
不过,值得一提的是,Google Analytics 4 现在会自动处理此问题,因此您不必自己实现。
尽管如此,了解useLocation
挂钩仍然很有价值,因为您可能会发现它的其他用途。例如,您可以使用它与其他库和分析服务集成,或者根据访问的路线执行特定操作。
可能性是无止境!
useLocation
挂钩非常方便,因为它为您提供有关当前正在使用的路线的信息。
以下是有关如何使用 useLocation 挂钩的友好指南:
import {BrowserRouter, Routes, Route, useLocation} from "react-router-dom"; import {createRoot} from "react-dom/client"; function App() { const location = useLocation(); console.log(location.pathname); return <Routes> {/* routes here... */} </Routes> } createRoot(document.querySelector("#react-root")).render(<BrowserRouter><App /></BrowserRouter>);
现在,您可以通过位置变量访问路线信息。它是一个包含当前pathname
的对象。
要找出用户当前正在浏览的路线,只需使用location.pathname
。它会给你类似/
、 /about
或你设置的任何其他路由的信息。
请记住,就像使用useNavigate
挂钩一样,让您的代码在<BrowserRouter />
包装的组件内运行非常重要。否则, useLocation
挂钩将无法发挥其魔力。
当位置发生变化时,您可以使用useLocation
钩子来访问 React Router 中的当前 URL 路径。通过使用useEffect
挂钩包装代码并将依赖项设置为location
,只要导航到新 URL,您就可以运行特定的代码片段。
import React, {useEffect} from "react"; import {BrowserRouter, Routes, Route, useLocation} from "react-router-dom"; import {createRoot} from "react-dom/client"; function App() { const location = useLocation(); // run a piece of code on location change useEffect(() => { console.log(location.pathname); // send it to analytic, or do some conditional logic here }, [location]); return <Routes> {/* routes here... */} </Routes> } createRoot(document.querySelector("#react-root")).render(<BrowserRouter><App /></BrowserRouter>);
每当用户在导航过程中移动到新路线时, useEffect
挂钩内的代码就会运行并执行其操作。它基本上是一种确保每当位置对象发生更改时都会执行特定代码的方法。
总而言之,本文让您对 React Router 有了更深入的了解。虽然您可能不会使用此处讨论的所有功能。不过,你能意识到它们,真是太棒了。如果您发现自己将来需要合并类似的功能,您就知道该去哪里寻找。因此,请务必保存这篇文章或为其添加书签,以便于参考。请记住,如果您有任何疑问或需要进一步帮助,请随时在 Twitter 上与我联系。我是来帮忙的!