npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-tiny-router

v2.0.1

Published

A tiny React router

Downloads

17

Readme

react-tiny-router

Package Version Babel Macro

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 any Link or Router 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 the Router declaration that you specify. Much like React with JSX, you need to import this even if you're not using the object directly. TinyRouter itself is a React component that accepts a singular ast 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 the History provider that the route needs to change.
  • import Router - Router is a babel macro that will replace itself with an instance of TinyRouter when compiled.

Now, for the actual body of the application:

  • <History> - You need to wrap anything that will use Link or Router tags with this component in order for them to have access to necessary context.
  • <Router> - All child components within a Router declaration should have either a path prop (which is the template URL to match) or a default prop (which indicates which component to render should no match be found). The child components can be any valid React component. They will receive as props any dynamic segments specified in the template URL. (In the example above, the Racer component will receive a name 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.