react-tiny-router
v2.0.1
Published
A tiny React router
Downloads
17
Readme
react-tiny-router
A tiny router for React.js.
Getting started
react-tiny-router
is a router for React.js
projects. It aims to be a small and out of the way as possible. Under the hood it uses babel-plugin-macros
to precompile the paths that you provide to ensure it can route as efficiently as possible. In order for this to work, your build process will need to be set up with the babel macros plugin.
Here's an example application that we can look at in detail:
import React from "react";
import { History, TinyRouter, Link } from "react-tiny-router";
import Router from "react-tiny-router/router.macro";
const Maps = () => ...;
const Racer = ({ name }) => ...;
export default const MarioKart = () => (
<History>
<h1>MarioKart</h1>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/maps">Maps</Link></li>
<li><Link to="/racer/toad">Toad</Link></li>
<li><Link to="/racer/yoshi">Yoshi</Link></li>
<li><Link to="/racer/peach">Peach</Link></li>
</ul>
<Router>
<p path="/">Welcome!</p>
<Maps path="/maps" />
<Racer path="/racer/:name" />
<p default>Looks like you made a wrong turn!</p>
</Router>
</History>
);
A lot of things are happening here, so let's break it down, starting with the imports:
import { History }
-History
is the context provider that needs to wrap anyLink
orRouter
tags. It maintains the state of the current path as well as provides various callbacks to manipulate the state (discussed later).import { TinyRouter }
-TinyRouter
is the router that ends up replacing theRouter
declaration that you specify. Much likeReact
withJSX
, you need to import this even if you're not using the object directly.TinyRouter
itself is aReact
component that accepts a singularast
prop, which is the compiled version of the routes that you declare.import { Link }
-Link
is a very simple<a>
tag that when clicked will inform theHistory
provider that the route needs to change.import Router
-Router
is ababel
macro that will replace itself with an instance ofTinyRouter
when compiled.
Now, for the actual body of the application:
<History>
- You need to wrap anything that will useLink
orRouter
tags with this component in order for them to have access to necessary context.<Router>
- All child components within aRouter
declaration should have either apath
prop (which is the template URL to match) or adefault
prop (which indicates which component to render should no match be found). The child components can be any validReact
component. They will receive as props any dynamic segments specified in the template URL. (In the example above, theRacer
component will receive aname
prop from the URL.)<Link>
-Link
components function very similarly to anchor tags, and should be treated the same.
Internals
Internally when babel
compiles this file, the JSX expressions inside of the Router
component become a large ast
object that becomes a prop to the TinyRouter
component that replaces the Router
. So in the above example, the entire Router
expression gets replaced by:
<TinyRouter
ast={{
render: () => <p>Welcome!</p>,
next: {
maps: {
render: () => <Maps />
},
racer: {
next: {
":dynamic": {
render: name => <Racer name={name} />
}
}
}
},
default: () => <p>Looks like you made a wrong turn!</p>
}}
/>
This ast
prop is then used by walking through each segment of the current URL (split by parentheses). For each segment, it will check the next
object for a key that matches the segment. If found, it will move into that subtree and continue. Once all of the segments are exhausted, it will look for a render
function at that node in the tree to know what to render.
Dynamic route segments are handled effectively the same way, except that they have a special :dynamic
key which the router will fall back to if a more specific route is not matched first. In this case the dynamic segment becomes a prop on the route's component.
useRouter
In case you need direct access to the routing context (for instance from a button
that will redirect when clicked), you can use the useRouter
named export. Example usage is below:
import React from "react";
import { History, TinyRouter, useRouter } from "react-tiny-router";
import Router from "react-tiny-router/router.macro";
const Racer = ({ name }) => ...;
const TextInput = ({ onChange, value }) => ...;
const RacerSearch = () => {
const { onPathChange } = useRouter();
const [search, setSearch] = useState("");
const onButtonClick = useCallback(
() => onPathChange(`/racer/${search}`),
[onPathChange, search]
);
return (
<>
<button type="button" onClick={onButtonClick}>
Select Your Player
</button>
<TextInput onChange={setSearch} value={search} />
</>
);
};
const MarioKart = () => (
<History>
<h1>MarioKart</h1>
<Router>
<RacerSearch path="/" />
<Racer path="/racer/:name" />
</Router>
</History>
);
export default MarioKart;
In this case we're using the useRouter
hook to get access to the onPathChange
callback. Then, when our text input is set to the correct value and the button is clicked, the route will change and the router will be rerendered.
useRouter
also gives you access to currentPath
(the current path in state) and onPathReplace
(which will replace the path in history as opposed to just pushing onto the stack).
eslint
Depending on your configuration, eslint
may get mad at you for importing TinyRouter
without it thinking you're using it in your source file. Rest assured, it does get used. To get rid of the linting error, you can add:
"no-unused-vars": ["error", { "varsIgnorePattern": "TinyRouter" }]
to the rules
section of your eslint
config.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/CultureHQ/react-tiny-router.
License
The gem is available as open source under the terms of the MIT License.