Routing in React - HL Vanilla Community
<main> <article class="userContent"> <div class="embedExternal embedImage display-large float-none"> <div class="embedExternal-content"> <a class="embedImage-link" href="https://us.v-cdn.net/6030677/uploads/T1YXJ4LC64PK/microsoftteams-image-288-29.png" rel="nofollow noreferrer noopener ugc" target="_blank"> <img class="embedImage-img" src="https://us.v-cdn.net/6030677/uploads/T1YXJ4LC64PK/microsoftteams-image-288-29.png" alt="MicrosoftTeams-image (8).png" height="108" width="1356" loading="lazy" data-display-size="large" data-float="none"></img></a> </div> </div> <p>Once <a href="https://success.vanillaforums.com/kb/articles/162-creating-pages-controllers" rel="nofollow noreferrer ugc">a </a><a href="https://success.vanillaforums.com/kb/articles/162-creating-pages-controllers" rel="nofollow noreferrer ugc"><code class="code codeInline" spellcheck="false" tabindex="0">PageDispatchController</code></a><a href="https://success.vanillaforums.com/kb/articles/162-creating-pages-controllers" rel="nofollow noreferrer ugc"> has been configured</a> and the server is serving a route, the frontend needs to be configured to mount something there.</p><p>From here the frontend router will attempt to handle as much routing as possible, until it it navigates to a route outside of its current section. Generally this is caused by a subcommunity or language change.</p><h2 data-id="overview">Overview</h2><p>We're going to build on the server side example of a custom resource with an "add" and "edit" route.</p><div class="js-embed embedResponsive" data-embedjson="{"body":"Looking to create routes containing some UI and content? This is the article for you! Differences from Gdn_Controller A PageDispatchController serves a different purpose to a Gdn_Controller. PageDispatchController Core static master view only. Primary views expected to be rendered in React. Server side rendering only of…","photoUrl":"https:\/\/us.v-cdn.net\/6030677\/uploads\/947\/TM5DAO1BWY5V.png","url":"https:\/\/success.vanillaforums.com\/kb\/articles\/162-creating-pages-controllers#creating-the-controller","embedType":"link","name":"Creating Pages & Controllers - Vanilla Success"}"> <a href="https://success.vanillaforums.com/kb/articles/162-creating-pages-controllers#creating-the-controller" rel="nofollow noreferrer ugc"> https://success.vanillaforums.com/kb/articles/162-creating-pages-controllers#creating-the-controller </a> </div><p>We will be making routes for <code class="code codeInline" spellcheck="false" tabindex="0">/my-prefix/resource/{id}/editor</code> and <code class="code codeInline" spellcheck="false" tabindex="0">/my-prefix/resource/add</code>.</p><h2 data-id="making-your-page-component">Making your page component</h2><p>The first step is to create our page. In our case we'll make a minimal form with a text area inside of a modal.</p><p>Because add & edit actions often use the same UI, we will be using a single Add/Edit component for both.</p><p><strong>src/scripts/pages/ResourceAddEditPage.tsx</strong></p><pre class="code codeBlock" spellcheck="false" tabindex="0">import React from "react"; import { TitleBar } from "@vanilla/library/src/scripts/headers/TitleBar"; import { useParams } from "react-router-dom"; export function ResourceAddEditPage() { const { id } = useParams(); useEffect(() => { if (id != null) { // Go fetch information about the ID from somewhere. // Don't forget to validate it. requestDataFromWhere(); } }, [id]); const data = useDataFromSomewhere() return ( <Modal isFullPage={true} size={ModalSizes.FULL_SCREEN}> <h1>{id == null ? "Add" : "Edit"}</h1> <form> <textarea name="body" placeholder="Hello world"></textarea> <button type="submit">Submit</button> </form> </Moadl> ); } </pre><p>The ID passed here will be the one from our URL. If there is on ID we know this is the Add page instead of edit, and don't fetch any initial data.</p><h2 data-id="creating-the-routehandler">Creating the RouteHandler</h2><p>Now that we have the component we have to create a route handler to handle the routing for it.</p><p>A <code class="code codeInline" spellcheck="false" tabindex="0">RouteHandler</code> handles forwards and reverse routing so it needs the following information:</p><ul><li>A loadable instance of your page component. This should a by a dynamic webpack import so the page contents can be code-split into their chunk.</li><li>Any valid URL path or array of paths that <a href="https://github.com/pillarjs/path-to-regexp/tree/v1.7.0" rel="nofollow noreferrer ugc"><code class="code codeInline" spellcheck="false" tabindex="0">path-to-regexp@^1.7.0</code></a> understands.</li><li>A function to generate a URL to this page from a set of parameters.</li><li>Optionally a special loader while the page's code is fetched. There are currently 2 built in loaders:<ul><li><code class="code codeInline" spellcheck="false" tabindex="0"><Loader /></code> - A standard circular loader, with a titlebar at the top.</li><li><code class="code codeInline" spellcheck="false" tabindex="0"><ModalLoader /></code> - A standard circular loader inside of a fullscreen white overlay. This will block out all background contents including header, titlebar, footer, etc.</li></ul></li></ul><p><strong>src/scripts/pages/ResourceAddEditRoute.tsx</strong></p><pre class="code codeBlock" spellcheck="false" tabindex="0">export const ResourceAddEditRoute = new RouteHandler( () => import( /* webpackChunkName: "pages/resourceAddEdit" */ "../ResourceAddEditPage.tsx" ), ["/my-prefix/resource/add", "/my-prefix/resource/:id(\\d+)/edit"], (id?: number) => ( id !== null ? `/my-prefix/resource/${id}/edit` : `/my-prefix/resource/add` ), ModalLoader ); </pre><p>Here we've:</p><ul><li>imported our page dynamically and gave it a chunk name for easier debugging.</li><li>Defined 2 URLs patterns for our page. Note that name <code class="code codeInline" spellcheck="false" tabindex="0">:id</code> means our param in the page will be named <code class="code codeInline" spellcheck="false" tabindex="0">id</code>.</li><li>Created a function generate the URLs.</li><li>Defined that these pages are contained in a full page modal, so the loader will appear similarly and prevent a flashing when the page finally loads.</li></ul><h2 data-id="registering-the-route">Registering the Route</h2><p>Now that we've created the route we can register it. We need to create an entry file that matches the entry declared <a href="https://success.vanillaforums.com/kb/articles/162-creating-pages-controllers#defining-an-asset-section" rel="nofollow noreferrer ugc">in our server controller's </a><a href="https://success.vanillaforums.com/kb/articles/162-creating-pages-controllers#defining-an-asset-section" rel="nofollow noreferrer ugc"><code class="code codeInline" spellcheck="false" tabindex="0">getAssetSection()</code></a>.</p><p>The example there was <code class="code codeInline" spellcheck="false" tabindex="0">admin</code>, so we are going to create the following file to register our route.</p><p><strong>src/scripts/entries/admin.tsx</strong></p><pre class="code codeBlock" spellcheck="false" tabindex="0">import { Router } from "@library/Router"; import { ResourceAddEditRoute } from "../pages/ResourceAddEditRoute.tsx"; Router.addRoutes([ ResourceAddEditRoute.route ]); </pre><p>With that done we should be able to navigate to <code class="code codeInline" spellcheck="false" tabindex="0">/my-prefix/resource/add</code> and see our page loaded!</p><h2 data-id="navigating-to-the-page">Navigating to the Page</h2><p>There a multiple ways to link to our page, they'll be listed here in order of preference.</p><p><strong>Using the generated link component</strong></p><pre class="code codeBlock" spellcheck="false" tabindex="0"><ResourceAddEditRoute.Link data={undefined} /> <ResourceAddEditRoute.Link data={5} /> </pre><ul><li>Does dynamic navigation to the route.</li><li>Type-checks the data to ensure a well-formed URL.</li><li>Automatically preloads the dynamic import on hover of the link.</li></ul><p><strong>Use the generated URL function</strong></p><pre class="code codeBlock" spellcheck="false" tabindex="0">ResourceAddEditRoute.preload(); // Manually Preload the route if possilbe.. ResourceAddEditRoute.url(undefined); ResourceAddEditRoute.url(5); </pre><ul><li>Does dynamic navigation to the route.</li><li>Type-checks the data to ensure a well-formed URL.</li><li>Possible to manually preload the route.</li></ul><p><strong>Use SmartLink</strong></p><pre class="code codeBlock" spellcheck="false" tabindex="0"><SmartLink to={resourceFromApi.url} /> <SmartLink to="/my-prefix/resource/add" /> <SmartLink to="/my-prefix/resource/5/edit" /> </pre><ul><li>Does dynamic navigation to the route when possible.</li><li>Doesn't have type checking when manually constructing a string.</li><li>Ideal for API based resources.</li></ul><p><strong>Normal link</strong></p><pre class="code codeBlock" spellcheck="false" tabindex="0"><a href="/my-prefix/resource/add" /> <a href="/my-prefix/resource/5/edit" /> </pre><ul><li>Does a full page reload and navigation.</li><li>Doesn't have type checking when manually constructing a string.</li></ul><h2 data-id="additional-resources">Additional Resources</h2><p>Vanilla's router is built on top of react-router, so any resources related to that may be useful.</p><div class="js-embed embedResponsive" data-embedjson="{"body":"Learn once, Route Anywhere","url":"https:\/\/reacttraining.com\/react-router\/web\/api\/Hooks","embedType":"link","name":"React Router: Declarative Routing for React"}"> <a href="https://reacttraining.com/react-router/web/api/Hooks" rel="nofollow noreferrer ugc"> https://reacttraining.com/react-router/web/api/Hooks </a> </div><p><br></p> </article> </main>