A declarative router for effector
and react
. It is inspired by react-router-dom
and effector gates. Routes are independent from each other and do not rely on React tree. Url manipulations are fully abstracted out, you only need to set path when creating a route.
Example: CodeSandbox
const HomeRoute = createRoute("/");
const UsersRoute = createRoute("/users");
const UserRoute = createRoute(`${UsersRoute.path}/:userId`);
const loadUsersFx = createEffect();
const $users = restore(loadUsersFx.doneData, []);
sample({ clock: UsersRoute.open, target: loadUsersFx });
const $currentUser = combine($users, UserRoute.match, (users, match) =>
users.find((user) => user.id === match.params.userId)
);
const UsersPage = () => {
const list = useList($users, ({ name, id }) => (
<li>
<Link to={UserRoute} params={{ userId: id }}>
{name}
</Link>
</li>
));
return (
<>
<h1>List of users</h1>
{list}
</>
);
};
const UserPage = () => {
const user = useStore($currentUser);
if (!user) {
return null;
}
const { name, avatar, email } = user;
return (
<>
<h1>{name}</h1>
<img src={avatar} alt={name} />
<a href={`mailto:${email}`}>{email}</a>
<hr />
<Link to={UsersRoute}>Back</Link>
</>
);
};
const App = () => {
return (
<>
<nav>
<Link to={HomeRoute}>Home</Link>
<Link to={UsersRoute}>Users</Link>
</nav>
<Switch>
<HomeRoute>
<h1>Hello</h1>
</HomeRoute>
<UsersRoute>
<UsersPage />
</UsersRoute>
<UserRoute>
<UserPage />
</UserRoute>
</Switch>
</>
);
};
Route
could be created using createRoute
.
const MyRoute = createRoute("/my/path");
const RelativeRoute = createRoute(`${MyRoute.path}/something/else`);
const RouteWithParams = createRoute<"id">("/users/:id");
createRoute
return React element, that you can use to conditionally render content.
<MyRotute>Page content</MyRoute>
// Or
<MyRoute exact>Will render only when exact match</MyRoute>
Store with current match of the route. Similar with Gate.state
.
url RouteWithParams.match
/users/abc { params: { id: "abc" }, isExact: true }
/users/abc/friends { params: { id: "abc" }, isExact: false }
/somewhere/else null
Triggers when route is matched. Similar with Gate.open
.
Triggers when route is leaving the route. Similar with Gate.close
.
Boolean store which shows if given route is currently matched. Similar with Gate.status
.
Event to navigate to the route. Accepts NavigateOptions
.
RouteWithParams.navigate(
// params?: Partial<PathParams<PathParamKeys>>;
params: {id: "xyz"},
// search?: boolean | string;
search: "query=newValue", // will update value
search: true, // will keep the current search value
// hash?: boolean | string;
hash: "hello", // will update value
hash: true, // will keep the current hash value
// method?: HistoryMethod;
method: "push", // default, will push to the history
method: "replace", // will replace the location in the history
)
You don't need to provide all the params if some of them are already in the url.
cosnt LongUserRoute = createRoute("/:locale/teams/:teamId/users/:userId");
// current url: /en/teams/123/users/abc
LongUserRoute.navigate({params: {userId: "xyz"}})
// new url: /en/teams/123/users/xyz
Renders a
tag, accepts NavigateOptions
as props.
<Link to={LongUserRoute} params={{ teamId: "456", userId: "abc" }}>
Petya
</Link>
Once rendered, it will navigate to a new location. By default it will replace the location in the history. You can pass to Redirect
same props as to Link
.
if (!hasAccess) {
return <Redirect to={HomeRoute} />;
}
CurrentRoute
is a helper for such cases when you want to only update search
or hash
. It implements only one event from the original Route
. It's CurrentRoute.navigate(options: NavigateOptions)
.
CurrentRoute.navigate({search: "query=newValue", hash: "hello"})
// Or using `Link`
<Link to={CurrentRoute} search="query=newValue" hash="hello">Update query</Link>
Renders the first matched route. If you provide component which is not Route
, it will be treated as match. It could be usefull to render 404 pages.
<Switch>
<UsersRoute exact>List of users</UsersRoute>
<UserRoute>User info</UserRoute>
<NotFoundPage />
</Switch>
A store with current search
.
A store with curernt hash