React Router v4
React Router v4 isn't like most other routers because it lacks a centralized configuration. Migrating to Curi mostly involves re-centralizing your routes to simplify route management.
Routes
Let’s get started with setting up our routes.
With React Router
With React Router v4, Route
s are defined in components. They are usually grouped together under a Switch
so that only a single route from a group renders. Nested routes are rendered inside of the compnent rendered by the parent Route
With Curi
Routes in Curi are JavaScript objects. They are grouped together in an array of "top-level" routes. Nested routes are grouped under their parent route's children
property.
First, we will define the names and paths for our routes.
Each route must have a unique name. A route's name will be used for interacting with it. For example, to navigate to a route, you only have to know its name, not its URL.
The biggest difference between the Curi paths and the React Router paths is that with Curi, you never include a forward slash at the beginning of the path. This means that while the root path for React Router is '/'
, the root path for Curi is ''
.
Next, we should add our components to each route.
Curi routes can have a respond
property, which is a function that returns an object of properties to merge onto the response that we will be using to render. For this React application, we want a response's body
property to be the React component associated with each route.
With React Router v4, a component's lifecycle methods are used for loading data, code splitting, and other non-rendering tasks. With Curi, routes can have a resolve
function that is called when thee routes matches the new location.
The @curi/router
route API documentation covers all of the route properties.
Once your routes have been defined, you can move on to creating your Curi router.
Creating the router
With React Router, you create your router by rendering a Router
component. This may be a BrowserRouter
, a HashRouter
, a MemoryRouter
, or a plain Router
that you pass your own history
instance to. The ___Router
components create a history
instance for you using props passed to the component.
With Curi, the router is created prior to rendering. It takes a Hickory history function, your routes array, and possibly an options object. Hickory is similar to the history
package used by React Router, but has an API tailored for asynchronous applications.
Rendering
We will walk through the rendering differences between React Router and Curi by looking at what happens in each when we navigate to the URL with the pathname /inbox/test
.
React Router
React Router matches routes while it renders. It uses the Router
component to listen for location changes. Each time that the location changes, the application re-renders.
The Switch
will iterate over its children Route
s. The first route, "/"
has an exact
prop, so it only matches when the pathname is "/"
. Since it is not, the next Route
will be checked. The next route, "/inbox"
matches the beginning of the pathname "/inbox/test"
. It is not an exact match, but that route does not do exact matching, so React Router will render its component, Inbox
.
The Inbox
has its own Switch
to iterate over. Its first route only matches "/inbox"
exactly, so it moves on to the next route, which has a message
route param. This route matches and stores "test-message-please-ignore"
as match.params.message
. The Message
component will then be rendered, which has access to the message
param.
Curi
With Curi, we also need to re-render our application every time that the location changes. We will do this by creating a root Curi component by calling the createRouterComponent
function, which comes from the @curi/react-dom
package, and passing it our Curi router. While the name of this component is entirely up to you, we will refer to it as the Router
here.
The Router
will setup an observer on the provided router so that it can re-render your application whenever there is a new response
. The Router
uses a context provider that makes a response available to other components in the application using the useResponse
hook.
The useResponse
hook returns an object with two properties:
response
is the new response objectnavigation
is an object with additional information about the navigation
The router can also be accessed throughout the application using the useRouter
hook.
Above, we added respond
functions to each route. The functions set React components as the body
property of responses. We can now use response.body
to render those components.
In the React Router section, we had three components that were rendered: App
,Inbox
, and Message
. With Curi, only the most accurately matched route actually matches. That means that for the URL /inbox/test
, the "Message"
route will match, but its parent route, "Inbox"
will not, so response.body
will be the Message
component. Unlike React Router, we don’t render Inbox
because we did not match the inbox
route.
It was mentioned above that there is no need for the App
component with Curi. If you want to have an App
component, you can render it either inside of the children
function or as a parent of your Router
. This can be useful for rendering content that is unrelated to specific routes, like a page header or menu.
Rendering the App
inside of the children
function is necessary if any of the components rendered by the App
are location aware components, since they need to access the Curi router (through React’s context, which the Router
provides)
What about props that you want to send to your route components? Pass them to the Body
component that you render. Props can be passed individually, but passing the whole response
object is recommended.
Links
You will want to be able to navigate between routes in your application. React Router provides a Link
component to do this, and so does Curi (through the @curi/react-dom
package). There are a few differences to note between these two components:
React Router expects you to generate the pathname yourself, while Curi expects you to pass the name of the route that you want to navigate to. Any path parameters are passed to Curi’s
Link
using theparams
prop.With React Router, any additional location properties are passed to the
Link
using theto
object. With Curi, these properties are passed using the prop name (hash
,query
&state
).Active detection with Curi is done using the
useActive
hook. The hook takes the name of the route (and any required params) and returns a boolean to indicate if the route is active. You can also use itspartial
option to detect when ancestor routes are active (the opposite of React Router'sonlyActiveOnIndex
).
Accessing router props from nested components
React Router provides a withRouter
higher-order component that will inject router props into the wrapped component.
Curi provides similar functionality with the ResponseConsumer
component.
The best way to get router data with Curi is to use the useResponse
hook.
At this point, hopefully you are comfortable with migrating from React Router v4 to Curi. If there are any concepts not covered here that you think should be, please feel free to open up an issue on GitHub.