Chronik
A simple, no-Context React-Redux client-side router based on the History Web API.
This project began as, and still is, an experiment and learning exercise of how one would implement routing in React-Redux using only the History Web API and without using Context.
Bug reports and constructive feedbacks are welcomed and would be much appreciated. :)
Table of Contents
Installation
NPM Package
Chronik is available as an NPM package:
npm install --save chronik
Connect Chronik to the Redux Store
// src/store.js import combineReducers createStore applyMiddleware from 'redux';import thunk from 'redux-thunk';import reducer as chronik from 'chronik'; const reducer = ; const store = ; ;
Initialise Chronik as a Component
Once Chronik is initialised, the Route
and Link
components will function anywhere within an app and do not have to be nested inside the Chronik
component.
import React from 'react';import ReactDOM from 'react-dom';import Provider from 'react-redux';import Chronik from 'chronik'; import store from './store'; ReactDOM;
If style is of concern, children inside the Chronik
component are rendered normally:
ReactDOM;
Quickstart
import Route Link Redirect navigate from 'chronik'; // ... // Match '/blog'<Route ="/blog" = /> // Match anything that begins with '/blog/'<Route ="/blog/*" = /> // Match anything that is not '/blog'<Route ="/blog" = /> // Create a link to a location within the app (use <a> for external links)<Link ="/">Home</Link> // Redirect a user upon rendering<Redirect ="/" /> // Redirect user if no paths can be matched<NoMatch ='/404' /> // Return component if no paths can be matched<NoMatch ='<NotFound />' /> // Programmatic navigation (navigate is an action creator, the code below// assumes that it has been hooked up with react-redux's `connect()`) { thisprops;} <button ="button" =>Register</button>
Usage
Route
Component
The Route
component accepts a compulsory path
prop as a string. If this reference string matches that of browser (window.location.pathname
), the element specified in the compulsory prop component
will be rendered.
It should be noted that the Route
component matches the string provided to the path
prop exactly—that is, for path='/cat/subcat'
, '/cat/subcat'
is the only match; in contrast, '/cat'
is not a match.
The Route
component is connected to the Redux store set up above and be used anywhere in an application simply by importing it:
import Route from 'chronik';
Basic Paths
Return <Meow />
if the current path (window.location.pathname
) is exactly '/cat'
:
<Route ="/cat" = />
Paths with Parameters
Return <Meowtwo />
if the current path (window.location.pathname
) is exactly '/cat/' + variable
:
<Route ="/cat/:subcat" = />
Accessing Pathname and Parameters in a Returned Component
If the Route
component returns a component that is not a simple DOM element, the string initially assigned to the path
prop of the Route
component and any parameters (if used) are passed into the returned component as a prop called routed
. Parameters specified in path
must be unique.
The routed
prop is an object structured as shown in the example below for a component that is returned by <Route path="/nummern/:eins/:zwei/:drei" component={<Meowdrei />} />
when visiting '/nummern/1/2/3'
:
// the routed prop inside <Meowdrei />
{
routed: {
pathname: /nummern/1/2/3,
params: {
eins: 1,
zwei: 2,
drei: 3
}
}
}
An illustrative example that yields 'Received 42
cats from /cat/42
.' when the client navigates to /cat/42
:
import React from 'react';import ReactDOM from 'react-dom';import Provider from 'react-redux';import Chronik from 'chronik'; import store from './store'; const Meow = routed const params pathname = routed; return <div> Received <code>paramsamount</code> cats from <code>pathname</code> </div> ;; ReactDOM;
Match the Beginning of a Path
Return <Meowthree />
if the current path (window.location.pathname
) begins with '/cat/'
:
<Route ="/cat/*" = />
Excluding a Path
The Route
component accepts an optional not
prop as a boolean, which causes the Route
component to match everything but the path specified.
<Route ="/" = />
Link
Component
The Link
component provides a means to navigate within an app that is consistent with the behaviour typically expected of a React app, while maintaining typical browser behaviour (using the forward/backward buttons, in particular).
For internal links, the Link
component should be used in place of the anchor DOM element (<a>
) unless the default behaviour of <a>
is desired.
To use the Link
component, simply import it:
import Link from 'chronik';
Creating a hyperlink with the Link
component is effectively the same as using the anchor DOM element:
<Link ="/cat">Cat</Link>
Redirect
Component
The Redirect
component facilitates simple redirection in the render() method of a React component and is mainly intended for conditional rendering.
To use the Redirect
component, simply import it:
import Redirect from 'chronik';
<Redirect ="/" />
NoMatch
Component
This is currently an experimental component.
The NoMatch
component can be used to either redirect a user or render a component when a path requested cannot be matched to any Route
component inside the scope of ReactDOM.render()
.
It should be noted that the NoMatch
component takes into account of all Route
components that can potentially be rendered. For this reason, currently only one NoMatch
component per-app is recommended and it should be placed as high as possible in the component tree.
Being using the NoMatch
component by importing it:
import NoMatch from 'chronik';
Redirect if No-match
<NoMatch ='/404' />
Return Component if No-match
<NoMatch = />
Programmatic Navigation
A navigate(path)
action creator is available from the Chronik package for programmatic navigation:
import navigate from 'chronik';
Example usage:
import React from 'react';import connect from 'react-redux'; import navigate from 'chronik'; Component { thisprops; } { return <button ="button" =>Register</button> ; } const mapDispatchToProps = dispatch return null mapDispatchToPropsRegister;
The window.history.go()
and window.history.back()
methods of the History Web API are also fully compatible with Chronik.
In use cases where Chronik's navigate()
or the History Web API's window.history.go()
and window.history.back()
are not applicable, Chronik-compatible programmatic navigation can be achieved by doing both of the following:
- Change the browser's URL with
window.history.pushState()
to the desired path - Dispatch an action to modify
state.chronik.pathname
in the Redux store
Code Examples
Basic routing
A simple app with three pages: home ('/'), blog ('/blog') and about ('/about').
import React from 'react';import ReactDOM from 'react-dom';import Provider from 'react-redux';import Chronik Route from 'chronik'; import store from './store'; const Home = return <div>Home</div> ;; const Blog = return <div>Blog</div> ;; const About = return <div>About</div> ;; ReactDOM;
Mixing Routes with Fixed Components
This is an extension of the basic example above, with fixed elements (header and footer) that is rendered on all three pages mixed in.
import React from 'react';import ReactDOM from 'react-dom';import Provider from 'react-redux';import Chronik Route from 'chronik'; import store from './store'; const Header = return <div>Header</div> ;; const Footer = return <div>Footer</div> ;; const Home = return <div>Home</div> ;; const Blog = return <div>Blog</div> ;; const About = return <div>About</div> ;; ReactDOM;
Redirect User by Conditional Rendering
import React from 'react';import ReactDOM from 'react-dom';import connect from 'react-redux';import Redirect from 'chronik'; import store from './store'; Component { const authenticated = thisprops; if authenticated return <Redirect ="/dashboard" /> return <form> <!-- Sign in form --> </form> ; } const mapStateToProps = state return authenticated: stateauthenticated ; mapStateToProps nullSignIn;
Changelog
Chronik uses semantic versioning.
- 1.0.0—Released Chronik!
- 1.0.1—Added code examples to README.md. Added license.
- 1.1.0—Optional
not
prop is now available to theRoute
component; which causes aRoute
element to match all but thepath
specified. - 1.2.0—The
Route
component can now perform a "begins-with" match if the string specified for itspath
prop has a trailing asterisk. - 1.2.1—README.md fixes.
- 1.3.0—Added
NoMatch
component. - 1.3.1—README.md fixes.
- 1.3.2—Minor README.md edits: added
NoMatch
to the Quickstart guide, minor edits. - 1.3.3—Fixed incorrectly scoped return statement in the
Route
component, which lead a component associated with anot
route to be briefly rendered on page load. FixedPropType
forchildren
to accommodate non-stringchildren
. - 1.4.0—Added
Redirect
component and the corresponding sections in README.md. Fixed incorrect code in README.md.