0.0.3 • Published 5 years ago

@openovate/webpack-react-router v0.0.3

Weekly downloads
2
License
MIT
Repository
github
Last release
5 years ago

webpack-react-router

Webpack powered React router that supports virtual modules, virtual folders, and code splitting.

Install

$ npm install --save-dev @openovate/webpack-react-router
$ npm install --save-dev history

Additionally you may want to make sure your react environment is properly setup. If your doing this from scratch, install the following modules as well.

$ npm i --save-dev @babel/core @babel/plugin-syntax-dynamic-import  @babel/plugin-transform-react-jsx @babel/polyfill @babel/preset-env @babel/preset-react @babel/register babel-loader react react-dom webpack webpack-cli webpack-dev-server webpack-hot-middleware

Also make sure you add the following scripts in package.json

{
  "scripts": {
    "build": "webpack",
    "start": "webpack-dev-server --mode development --no-inline"
  }
}

Usage

Create a file called webpack.config.js in your project root.

//#FILE: webpack.config.js
const path = require('path');
const ReactRouterPlugin = require('@openovate/webpack-react-router');

module.exports = {
  mode: 'development',
  entry: {
    index: './client/index.js'
  },
  output: {
    filename: '[name].bundle.js',
    publicPath: '/',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: { presets: ['@babel/env'] }
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  },
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    compress: false,
    port: 3000,
    hot: false
  },
  resolve: {
    extensions: ['*', '.js', '.jsx']
  },
  plugins: [
    new ReactRouterPlugin({
      router: 'client/router.js',
      routes: {
        '/': './Hello.jsx',
        '/about': './About.jsx'
      }
    })
  ]
};

You will also need to create a .babelrc file in your project root.

{
  "presets": ["@babel/env", "@babel/preset-react"],
  "plugins": [
    "@babel/plugin-transform-react-jsx",
    "@babel/plugin-syntax-dynamic-import"
  ]
}

Create a file called dist/index.html in your project root.

<!DOCTYPE html>
<html>
  <head>
    <meta charSet="utf-8" />
    <title>Webpack React Router</title>
  </head>
  <body>
    <div id="root"></div>
    <noscript>
      You need to enable JavaScript to run this app.
    </noscript>
    <script src="/index.bundle.js"></script>
  </body>
</html>

Create a file called client/index.js in your project root.

//#FILE: client/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import router from './router'

const { createBrowserHistory } = require('history');

ReactDOM.render(
  router(createBrowserHistory()),
  document.getElementById('root')
)

./router is a virtual React component generated by this plugin. It requires that you use the history module or equivalent interface.

Create a component file called client/Hello.jsx.

//#FILE: client/Hello.jsx
import React from 'react'
import Link from './Link'

export default class Hello extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello World</h1>
        <ul>
          <li><Link to="/" history={this.props.history}>Home</Link></li>
          <li><Link to="/about" history={this.props.history}>About Us</Link></li>
        </ul>
      </div>
    )
  }
}

The router will pass 2 properties to this.props.

  • history - which is the initial history was passed in client/index.js
  • route - which will have the information of the route matched

Create a component file called client/About.jsx

//#FILE: client/About.jsx
import React from 'react'
import Link from './Link'

export default class About extends React.Component {
  render() {
    return (
      <div>
        <h1>About World</h1>
        <ul>
          <li><Link to="/" history={this.props.history}>Home</Link></li>
          <li><Link to="/about" history={this.props.history}>About Us</Link></li>
        </ul>
      </div>
    )
  }
}

Lastly, we need to create a component file called client/Link.jsx

//#FILE: client/Link.jsx
import React from 'react';

export default class Link extends React.Component {
  constructor(props) {
    super(props);
    //quirk that is recommended by react. lame.
    this.handle = this.handle.bind(this);
  }

  handle(event) {
    const { to, history } = this.props
    const props = this.props.props || {}

    event.preventDefault();

    history.push(to, props);
    return false;
  }

  render() {
    const { to, children } = this.props;
    const props = { href: to, onClick: this.handle };
    return React.createElement('a', props, children);
  }
}

Run $ npm start in terminal and open 127.0.0.1:3000 in your browser.

More Usage

see test/env in this repo for advance usage.

Route Paths

This plugin uses path-to-regexp which is used by react router and express

Why

Needed a react router that considered the following.

Dynamic Imports with Dynamic Pathing

The following example underlies this topic.

const routes = {
  '/': './screens/Hello.jsx',
  '/about': './screens/About.jsx'
}

import(routes['/']).then(component => {
  console.log(component.default)
})

Using dynamic import() in webpack 4 enables code splitting. When using dynamic pathing like the above, import(routes['/']) simply won't work because webpack could not predetermine the actual value when compiling.

For some reason, wrapping a JS template seems to work like the following.

import(`${routes['/']}`).then(component => {
  console.log(component.default)
})

But for Virtually Defined Modules, this does not. Virtual Modules powers Virtual Folders. Virtual Folders allows folder resolution where webpack cannot find files at their specified location. With Virtual Folders, we could simply tell Webpack other places where it could be. This is ideal in the case where you may want to modularize your project using a particular file structure in an unopinionated way.

For the reasons above, a plugin that generates a virtual router was decided.