All Projects → mksarge → Redux Json Router

mksarge / Redux Json Router

Licence: mit
Declarative, Redux-first routing for React/Redux browser applications.

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to Redux Json Router

Miox
Modern infrastructure of complex SPA
Stars: ✭ 374 (+910.81%)
Mutual labels:  webpack, middleware, router
React Imported Component
✂️📦Bundler-independent solution for SSR-friendly code-splitting
Stars: ✭ 525 (+1318.92%)
Mutual labels:  webpack, code-splitting
Body Parser
Node.js body parsing middleware
Stars: ✭ 4,962 (+13310.81%)
Mutual labels:  middleware, json
Underconstruction
This Laravel 8 package makes it possible for you to set your website in "Under Construction" mode. Only users with the correct 4 (or more) digit code can access your site. 🔥 💥 🔥
Stars: ✭ 549 (+1383.78%)
Mutual labels:  webpack, middleware
Iris
The fastest HTTP/2 Go Web Framework. AWS Lambda, gRPC, MVC, Unique Router, Websockets, Sessions, Test suite, Dependency Injection and more. A true successor of expressjs and laravel | 谢谢 https://github.com/kataras/iris/issues/1329 |
Stars: ✭ 21,587 (+58243.24%)
Mutual labels:  middleware, router
Next Connect
The TypeScript-ready, minimal router and middleware layer for Next.js, Micro, Vercel, or Node.js http/http2
Stars: ✭ 454 (+1127.03%)
Mutual labels:  middleware, router
Gear
A lightweight, composable and high performance web service framework for Go.
Stars: ✭ 544 (+1370.27%)
Mutual labels:  middleware, router
Neutrino
Create and build modern JavaScript projects with zero initial configuration.
Stars: ✭ 3,844 (+10289.19%)
Mutual labels:  webpack, middleware
Redux Dynamic Modules
Modularize Redux by dynamically loading reducers and middlewares.
Stars: ✭ 874 (+2262.16%)
Mutual labels:  middleware, code-splitting
Gt React Scaffold
🥚A boilerplate for client apps of webpack, react, redux, router...
Stars: ✭ 15 (-59.46%)
Mutual labels:  webpack, router
That React App You Want
That react app you always wanted: [email protected], [email protected], postCSS, purifycss, dll's and code splitting examples, bregh. Highly opinionated but you better like it.
Stars: ✭ 27 (-27.03%)
Mutual labels:  webpack, code-splitting
React Router Server
Server Side Rendering library for React Router v4.
Stars: ✭ 443 (+1097.3%)
Mutual labels:  webpack, code-splitting
Koa Webpack
Development and Hot Reload Middleware for Koa2
Stars: ✭ 429 (+1059.46%)
Mutual labels:  webpack, middleware
Copper
Copper is a set of Go packages that help you build backend APIs quickly and with less boilerplate.
Stars: ✭ 35 (-5.41%)
Mutual labels:  middleware, router
Diet
A tiny, fast and modular node.js web framework. Good for making fast & scalable apps and apis.
Stars: ✭ 394 (+964.86%)
Mutual labels:  middleware, router
Rill
🗺 Universal router for web applications.
Stars: ✭ 541 (+1362.16%)
Mutual labels:  middleware, router
Express React Boilerplate
🚀🚀🚀 This is a tool that helps programmers create Express & React projects easily base on react-cool-starter.
Stars: ✭ 32 (-13.51%)
Mutual labels:  webpack, code-splitting
Krakend
Ultra performant API Gateway with middlewares. A project hosted at The Linux Foundation
Stars: ✭ 4,752 (+12743.24%)
Mutual labels:  middleware, router
Loadable Components
The recommended Code Splitting library for React ✂️✨
Stars: ✭ 6,194 (+16640.54%)
Mutual labels:  webpack, code-splitting
Apicache
Simple API-caching middleware for Express/Node.
Stars: ✭ 957 (+2486.49%)
Mutual labels:  middleware, json

Changes in version 1.x

State Shape

redux-json-router now uses the redux-first-routing package internally, following its state shape:

// URL: www.example.com/nested/path?with=query#and-hash
{
  router: {
    pathname: '/nested/path/',
    search: '?with=query',
    queries: {
      with: 'query'
    },
    hash: '#and-hash'
  },
  ... // other redux state
}

Link

The optional replace prop was removed. Instead, you can now specify the desired navigation action via the action prop:

<Link to="/about" />                   // default: dispatches a push action
<Link to="/about" action="replace" />  // dispatches a replace action
<Link action="goBack" />               // dispatches a goBack action
<Link action="goForward" />            // dispatches a goForward action

Redux JSON Router

build status npm version

redux-json-router is a minimal router intended for use with client-rendered React/Redux applications.

Features

  • Declarative routing — The routing configuration is defined declaratively with JSON, or with plain JavaScript.
  • Redux-first routing — The URL is just a regular part of Redux state, and can be updated by dispatching actions.
  • Practical handling of browser history — The URL held in the browser history and Redux store stay in sync behind the scenes, so that the UI can always trust the URL in the store as its source of truth.
  • Code-splitting — Code-splitting and asynchronous loading of routes is easily enabled with a webpack loader.
  • React bindings<Router/> for matching/loading/rendering routes, and <Link/> for push/replace navigation.

Quick Links

Motivation

Background

I know what you're thinking - yet another React/Redux router? Really?

Yes, there are many existing solutions out there already. Of course, you already know react-router. But you may have also heard of solutions like redux-little-router or universal-router.

Every router has its strengths - and, as with any library, choosing a router comes down to finding the one that best suits your needs. Here are some of the categories that similar routers target:

  • Environment: Browser-only, or universal
  • Libraries: React-only, Redux (React optional), or other
  • Routing config: JSX, plain JS, or JSON-based

redux-json-router similarly targets a subset of the above categories. It attempts to answer the question: What is the minimal API needed in a Redux-first router for client-rendered React/Redux applications with a JSON-based routing configuration?

Redux-First Routing

Key point: Like with any other complex/dynamic state, the application view should depend solely on the URL held in the Redux store (a single source of truth).

In modern browsers, the URL state and history are held in window.location and window.history. We can manipulate the browser history directly using the history API, but even better, we can utilize the awesome history module to accomplish this.

Without Redux-first routing, a React/Redux application view may depend on URL state from outside of the store, like this:

# using react-router:                                  
history → React Router ↘
                        View
                 Redux ↗

# using react-router + react-router-redux:
history → React Router ↘
                   ↕    View
                 Redux ↗

With Redux-first routing, a React/Redux application view depends solely on the URL from the Redux store.

# using redux-json-router:
history
    ↕      
  Redux → View 

redux-json-router accomplishes Redux-first routing by making the URL a regular part of the state tree, allowing Redux actions to dispatch URL changes, and keeping the store and browser history in sync behind the scenes.

Here's what the URL looks like on the state tree:

// current URL: https://www.website.com/redux/json/router?is=cool#yup
// previous URL: https://www.website.com/previous/route
{
  ..., // other redux state 
  router: {
    url: '/redux/json/router?is=cool#yup',
    hash: 'yup',
    queries: {
      is: 'cool'
    },
    paths: [
      'redux',
      'json',
      'router'
    ],
    previous: {
      url: '/previous/route',
      hash: '',
      queries: {},
      paths: [
        'previous',
        'route'
      ]
    }
  }
}

Installation

npm install --save redux-json-router

API

redux-json-router has a reasonably small API. Here's a look at the exports in src/index.js:

// History API
export { createBrowserHistory } from './history/createBrowserHistory';
export { startListener } from './history/startListener';

// Redux API
export { routerMiddleware } from './redux/middleware';
export { routerReducer } from './redux/reducer';
export { push, replace, go, goBack, goForward, manualChange } from './redux/actions';
export { PUSH, REPLACE, GO, GO_BACK, GO_FORWARD, MANUAL_CHANGE } from './redux/constants';

// React API
export { RouterContainer as Router } from './react/Router';
export { LinkContainer as Link } from './react/Link';

/* The webpack loader 'route-loader' is not exported here.
   Import it directly into your webpack config, following the instructions below. */
  • History API
  • Redux API
    • routerMiddleware(history)
      • intercepts router actions (push, replace, go, goBack and goForward) to update the browser history, before continuing/breaking the middleware chain
    • routerReducer
      • parses and adds the URL to the Redux state
    • routerActions
      • used to dispatch URL updates; middleware intercepts and calls equivalent history navigation actions
        • push(href) — updates the current and previous URL in the Redux state
        • replace(href) — updates the current URL, but not the previous URL, in the Redux state
        • go(index) — intercepted by routerMiddleware; startListener subsequently dispatches manualChange action
        • goBack() — intercepted by routerMiddleware; startListener subsequently dispatches manualChange action
        • goForward() — intercepted by routerMiddleware; startListener subsequently dispatches manualChange action
        • manualChange(href) — updates the current and previous URL in the Redux state
    • routerConstants
      • public action types for use in user-defined middleware
  • React components
    • <Router/>
      • matches the current URL with the routing configuration, loads, then renders the page
      • props:
    • <Link/>
      • used for internal navigation (as opposed to <a/>, for external navigation)
      • props:
        • to (string) — required; the internal URL (pathname + query + hash) to navigate to (eg. '/about', '/blog?posted=2017', '/blog/post/1#introduction')
        • replace (boolean) — optional; set to true for the link to dispatch a replace action (default: push)
        • onClick (function) — optional; specify a callback to run on click, before the push/replace action is dispatched
  • Webpack loader (optional)
    • route-loader
      • translates a .json routing configuration file into JavaScript; see Routing Configuration for acceptable JSON, and Webpack Configuration for set-up instructions
      • options:
        • debug (boolean) — optional; if true, logs the output to the console (default: false)
        • chunks (boolean) — optional; if true, splits routes without a specified chunk property into separate chunks; if false, adds pages without a specified chunk into the main code chunk (default: true)

Usage

Let's look at how we'd add redux-json-router to a React/Redux application. We'll only make a few changes:

  • Routing config — We'll define the routes in a routes.json or routes.js file.
  • Redux config — We'll add the Redux reducer and middleware to the store.js configuration file.
  • App entry point — We'll add a bit of boilerplate to index.js and render the app with <Router/>.
  • Webpack config (optional) — To load routes.json, we'll add a custom loader to webpack.config.js.

Routing Configuration

Declare your routes in a routes.json file with an array of "route objects":

// routes.json
[
  {
    "path": "/",  // an exact path
    "page": "./pages/Home",
    "chunk": "main"
  },
  {
    "path": "/docs",
    "page": "./pages/Docs",
    "chunk": "main",
    "children": [
      {
        "path": "/:id",  // a nested and parameterized path
        "page": "./pages/Post"
      }
    ]
  },
  {
    "path": "*",  // a catch-all path
    "page": "./pages/Error"
  }
]

Route objects are defined as follows:

type RouteObject {
  path: string,             // Required. Specifies the path name (options: exact, param, or catch-all).
  page: string,             // Required. Specifies the React component to be instantiated.
  chunk?: string,           // Optional. Specifies the code chunk to be loaded in (default: separate chunks for all pages).
  children?: [RouteObject]  // Optional. Specifies any nested routes.
}

The bundled webpack loader is used to translate routes.json into JavaScript that can be read by the <Router/> component. Alternatively, you may choose to write the routing config in JavaScript yourself:

// routes.js - equivalent to routes.json above
export default [
  {
    path: '/',
    load: () => Promise.resolve(require('./pages/Home').default), // file loaded in the main code chunk
  },
  {
    path: '/docs',
    load: () => Promise.resolve(require('./pages/Docs').default), // file loaded in the main code chunk
    children: [
      {
        path: '/:id',
        load: () => new Promise((resolve, reject) => {
          try {
            require.ensure(['./pages/Post'], (require) => { // file loaded in a separate code chunk
              resolve(require('./pages/Post').default);
            });
          } catch (err) {
            reject(err);
          }
        }),
      },
    ],
  },
  {
    path: '*',
    load: () => new Promise((resolve, reject) => {
      try {
        require.ensure(['./pages/Error'], (require) => { // file loaded in a separate code chunk
          resolve(require('./pages/Error').default);
        });
      } catch (err) {
        reject(err);
      }
    }),
  },
];

Redux Configuration

In your Redux config, add routerReducer to the root reducer under the router key, and add routerMiddleware to your Redux middlewares, passing it the history singleton created in the application entry point (as shown in the following section).

// store.js
import { combineReducers, applyMiddleware, compose, createStore } from 'redux';
import { routerReducer, routerMiddleware } from 'redux-json-router';
import { otherReducers, otherMiddlewares } from './other';

// add `routerReducer` to your root reducer
const makeRootReducer = () => combineReducers({
  ...otherReducers,
  router: routerReducer
});

function configureStore(history, initialState = {}) {
  // add `routerMiddleware` to your middlewares, passing it the history singleton from the app's entry point
  const middlewares = [...otherMiddlewares, routerMiddleware(history)];
  
  const enhancers = [applyMiddleware(...middlewares)];
  
  return createStore(makeRootReducer(), initialState, composeEnhancers(...enhancers));
}

Application Entry Point

In your app's entry point, import the routing config, create the history singleton, create the store with the history singleton, and call startListener to initialize the router state in the store and start listening for external actions. Finally, render the application using <Router/> inside the Redux <Provider/>.

// index.js
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { createBrowserHistory, startListener, Router } from 'redux-json-router';
import configureStore from './store';
import routes from './routes.json'; // webpack-loaded JSON routing config
// import routes from './routes'; // plain JavaScript routing config

// create a history singleton
const history = createBrowserHistory();

// configure store with history
const store = configureStore(history);

// dispatch actions when the history is manually changed (with navigation buttons / address bar)
startListener(history, store);

// render the application with <Router /> to match the current URL to the routing config
render(
  <Provider store={store}>
    <Router routes={routes} />
  </Provider>,
  document.getElementById('app'));

Webpack Configuration (Optional)

To use the included webpack loader, import route-loader directly into your webpack config:

// webpack.config.js
const routes = [path.resolve(__dirname, './app/routes.json')]; // the path to your JSON routing config
const config = {
  ...,
  module: {
    rules: [
      ...,
      {
        test: /\.json$/,
        exclude: routes, // exclude routes.json from being loaded by the usual json-loader
        loader: 'json-loader',
      },
      {
        test: /\.json$/,
        include: routes, // load routes.json with route-loader instead
        loader: 'redux-json-router/lib/route-loader',
        options: {
          // debug (boolean) - defaults to false
          // chunks (boolean) - defaults to true
        },
      },
      ...

Credits

This project was heavily inspired by similar work and research on JavaScript/React/Redux routing including:

Contributing

Contributions are welcome and are greatly appreciated!

Feel free to file an issue, start a discussion, or send a pull request.

License

MIT

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].