Route configuration
A route object under the default matching algorithm and route element resolver consists of the following properties, all of which are optional:
path: string
: a string defining the pattern for the routeComponent
orgetComponent
- the component for the route, or a method that returns the component for the route
Component: React.ComponentType<any>;
getComponent: (match: RouteMatch) =>
React.ComponentType<any> | Promise<React.ComponentType<any>>;
data
orgetData
: additional data for the route, or a method that returns additional data for the route
data: any;
getData: (match: RouteMatch) => any;
allowAsIndex: boolean
: Indicates a Route with children also functions as it's own index route.defer: boolean
: whether to wait for all parentdata
orgetData
promises to resolve before getting data for this route and its descendantsrender: (args: RouteRenderArgs): ResolvedElement | undefined
: a method that returns the element for the routechildren
: an array of child route objects, or an object of those arrays; if using JSX configuration components, this comes from the JSX children
A route configuration consists of an array of route objects.
You can generate such an array of route objects from JSX with <Route>
elements using makeRouteConfig
, as shown previously in quick start.
By default, <Route />
components receive the following additional props describing the current routing state:
match: Match
: an object with router state properties, conforming to thematchShape
prop type validator
interface Match<TContext = any> {
location: LocationDescriptor; // The current [`location object`](https://github.com/4Catalyzer/farce#locations-and-location-descriptors)
params: Params; // The union of path parameters for all matched routes
routes: RouteObject[]; // An array of all matched route objects
route: RouteObject; // The route object corresponding to this component
routeParams: Params[]; // The path parameters for `route`
routeIndices: RouteIndices;
context: TContext;
}
router: Router
: an object with static router properties, conforming to therouterShape
prop type validator
interface Router {
push: (location: LocationDescriptor) => void; // Navigates to a new location
replace: (location: LocationDescriptor) => void; // Replaces the existing history entry
go: (delta: number) => void; // Moves `delta` steps in the history stack
isActive: (
match: Match,
location: LocationDescriptor,
{ exact: boolean }
) => boolean; // For `match` as above, returns whether `match` corresponds to `location` or a subpath of `location`; if `exact` is set, returns whether `match` corresponds exactly to `location`
format: (pattern: string, params: ParamsDescriptor) => string;
addNavigationListener(
listener: (location: LocationDescriptor) => any,
{ beforeUnload: boolean }
); // Adds a [navigation listener](https://github.com/4Catalyzer/farce#navigation-listeners) that can [block navigation](#blocking-navigation)
}
The getComponent
method receives an object containing the same properties as the match
object above, with an additional router
property as above.
path
Specify a path
pattern to control the paths for which a route is active. These patterns are handled using Path-to-RegExp and follow the rules there. Both named and unnamed parameters will be captured in params
and routeParams
as below. The following are common patterns:
/path/subpath
- Matches
/path/subpath
- Matches
/path/:param
- Matches
/path/foo
withparams
of{ param: 'foo' }
- Matches
/path/:regexParam(\\d+)
- Matches
/path/123
withparams
of{ regexParam: '123' }
- Does not match
/path/foo
- Matches
/path/:optionalParam?
- Matches
/path/foo
withparams
of{ optionalParam: 'foo' }
- Matches
/path
withparams
of{ optionalParam: undefined }
- Matches
/path/*
- Matches
/path/foo/bar
- Matches
Routes are matched based on their path
properties in a depth-first manner, where path
on the route must match the prefix of the remaining current path. Routing continues through any routes that do not have path
set. To configure a default or "index" route, use a route with no path
.
Component
or getComponent
Define the component for a route using either a Component
field or a getComponent
method. Component
should be a component class or function. getComponent
should be a function that returns a component class or function, or a promise that resolves to either of those. Routes that specify neither will still match if applicable, but will not have a component associated with them.
data
or getData
Specify the data
property or getData
method to inject data into a route component as the data
prop. data
can be any value. getData
can be any value, or a promise that resolves to any value. getData
receives an object containing the routing state, as described above for getComponent
.
The getData
method is intended for loading additional data from your back end for a given route. By design, all requests for asynchronous component and data dependencies will be issued in parallel. Found uses static route configurations specifically to enable issuing these requests in parallel.
If you need additional context such as a store instance to fetch data, specify this as the matchContext
prop to your router. This context value will then be available as the context
property on the argument to getData
.
const route = {
path: "widgets/:widgetId",
Component: WidgetPage,
getData: ({ params, context }) =>
context.store.dispatch(Actions.getWidget(params.widgetId)),
};
// <Router matchContext={{ store }} />
It does not make sense to specify data
or getData
if the route does not have a component as above or a render
method.
allowAsIndex
Convenience prop for indicating a Route with children also functions as it's own index route.
<Route allowAsIndex path="customers" Component={Page}>
<Route path=":customerId" Component={ChildPage} />
</Route>
This is equivalent to when matching '/customers'
:
<Route path="customers" Component={Page}>
<Route Component={() => null} />
<Route path=":customerId" Component={ChildPage} />
</Route>
defer
By default, Found will issue all data fetching operations in parallel. However, if you wish to defer data fetching for a given route until its parent data promises has been resolved, you may do so by setting defer
on the route.
<Route Component={Parent} getData={getParentData}>
<Route Component={Child} getData={getChildData} defer />
</Route>
Setting defer
on a route will make the resolver defer calling its getData
method and the getData
methods on all of its descendants until all of its parent data promises have resolved.
This should be a relatively rare scenario, as generally user experience is better if all data are fetched in parallel, but in some cases it can be desirable to avoid making data fetching operations that are guaranteed to fail, such as when the user is not authenticated, when optimizing for client bandwidth usage or API utilization.
render
Specify the render
method to further customize how the route renders. It receives an object with the following properties:
It should return:
- another function that receives its children as an argument and returns a React element; this function receives
- a React element when not using named child routes
- an object when using named child routes
null
when it has no children
- a React element to render that element
undefined
if it has a pending asynchronous component or data dependency and is not ready to rendernull
to render its children (or nothing if there are no children)
Note that, when specifying this render
method, Component
or getComponent
will have no effect other than controlling the value of the Component
property on the argument to render
. Additionally, the behavior is different between returning a function that returns null
and returning null
directly; in the former case, nothing will be rendered, while in the latter case, the route's children will be rendered.
interface RenderProps {
match: Match; // The routing state object, as above
Component: React.ComponentType<any>; // The component for the route, if any; `null` if the component has not yet been loaded
props: Match & {data: any | null}; // `The default props for the route component, specifically `match` with `data` as an additional property; `null` if `data` have not yet been loaded
data: any; // The data for the route, as above; `null` if the data have not yet been loaded
}
render: (e: RenderProps) => React.ComponentType<any> | (children) => React.ComponentType<any> | null | undefined;
You can use this method to render per-route loading state.
function loadingRender({ Component, props }) {
if (!Component || !props) {
return <LoadingIndicator />;
}
return <Component {...props} />;
}
<Route render={loadingRender} {...rest} />;
If any matched routes have unresolved asynchronous component or data dependencies, the router will initially attempt to render all such routes in their loading state. If those routes all implement render
methods and return non-undefined
values from their render
methods, the router will render the matched routes in their loading states. Otherwise, the router will continue to render the previous set of routes until all asynchronous dependencies resolve.