Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using React Router and Webpack 2 how to require external libraries only on certain routes?

Currently working

Using Webpack 2 and React Router v4 I have been able to set up working code splitting. There is an intermediate <AsyncComponent> that resolves the promise and returns the component (a pattern found on github issues).

An example set of routes below:

<Switch>
    <Route 
        path="me" 
        render={(props) => 
            <AsyncComponent promise={ require.ensure([], (require) => require('./modules/Profile'), 'profile') } props={props} />
        } 
    />
    <Route 
        path="credit-card" 
        render={(props) => 
            <AsyncComponent promise={ require.ensure([], (require) => require('./modules/CreditCard'), 'credit-card') } props={props} />
        } 
    />
</Switch>

Objective

I would like to extend that further, and for certain routes only, load in additional libraries. In the example above, I would like to fetch the StripeJS (https://js.stripe.com/v2/) library only when on the credit card route.

I want to emphasise that I can load Stripe directly into the footer and all works normally. There are multiple libraries, I'm using Stripe as an easy to digest example.

Attempted

The following has been attempted to little success:

  • Marking the lib external within the webpack config. This (correctly) flags the library as being external and won't attempt to bundle it during the resolve sequence of a require. However the expectation is that the library is manually brought in.
  • I have played with the idea of using a pseudo script loader (when hitting that route, manually create a <script> with a src attribute, wait for it to load, then let the component do its thing. This works ok, but is really horrible from a maintainability (if two or more libraries are needed, I then need to replicate the clumsy manual script load) point of view and seems to work against the webpack "way".

Relevant parts of the config

const core = [
    'lodash',
    'react',
    'react-dom',
    'axios',
    'react-router-dom',
];

const config = {
    context: path.resolve(__dirname, './ts_build'),
    node: {
        fs: "empty"
    },
    entry: {
        app: './app.js',
        core: core,
    },
    output: {
        filename: '[name].js',
        chunkFilename: '[name].[id].chunk.js',
        path: path.resolve(__dirname, './../../public'),
        publicPath: 'http://example.org/',
    },
    resolve: {
        modules: [
            path.resolve('./src'),
        ],
    },
    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            names: ['core'], 
            minChunks: Infinity,
        }),
        new webpack.NamedModulesPlugin(),
    ],
};
like image 763
Chris Avatar asked Feb 13 '17 15:02

Chris


People also ask

How do you restrict access to routes in react?

To restrict access to routes in React Router, we set the render prop to a function that renders the component we want according to the condition we're checking. import { Route, Redirect } from "react-router"; <Route exact path="/" render={() => (loggedIn ?

How do you create a private route in react?

Path: /src/_components/PrivateRoute.jsx The react private route component renders child components ( children ) if the user is logged in. If not logged in the user is redirected to the /login page with the return url passed in the location state property.

Should you use link outside of Router?

You should not use route or withrouter() or Link outside a router. React router throws the error, “you should not use route or withrouter() outside a router” when you have not defined BrowserRouter and still using Link or NavLink or Router components.


1 Answers

https://github.com/hinok/webpack2-react-router-code-splitting

Repository contains webpack2 + react-router v4 + implemented code splitting using dynamic import() and loading external libraries on demand only once by using loadjs. As example it loads Stripe.js for specific route.

In repository you can find two ways to do code splitting

  • by route config
  • by component

It's based on official Webpack documentation and gist by Andrew Clark

  • https://webpack.js.org/guides/lazy-load-react/
  • https://gist.github.com/acdlite/a68433004f9d6b4cbc83b5cc3990c194

While preparing that repository I found that @Chris would like to load Stripe.js only for certain routes which is hosted on external CDN. Since then I've been thinking about the best way to use also AMD modules and avoid leaking global variables but this is a bit tricky because each AMD module could have different wrapper, by saying wrapper I mean something like:

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define(['b'], function (b) {
            return (root.amdWebGlobal = factory(b));
        });
    } else {
        root.amdWebGlobal = factory(root.b);
    }
}(this, function (b) {
    return {};
}));

I could say that UMD wrapper is kind of standard but sometimes people prefer less opinionated wrappers or they just don't care about other environments. I'm mentioning it because in the git history, you can find commit called Amdify which was more proof of concept how AMD env could be simulated. Finally I decided to remove it because it's far from ideal and it doesn't cover all usages and edge cases.

I couldn't find any information about integrating external AMD modules or using AMD modules in some way with webpack. On the contrary I found that it will just not work

@mc-zone you should be able to load AMD modules with script.js. But this has nothing to do with webpack, it just works because AMD was designed for this. Thus you won't be able to use webpack features inside these AMD modules. webpack requires static analysis on build time.

by @jhns see on github

webpack doesn't handle script loading. Use an separate library for this. i. e. https://github.com/ded/script.js/

by @sokra see on github

If it's not possible you should not require() it but load it via a script-loader like script.js.

by @jhns see on github

Related issues on Github:

  • https://github.com/webpack/webpack/issues/150
  • https://github.com/webpack/webpack/issues/118
  • https://github.com/webpack/webpack/issues/462
  • https://github.com/webpack/webpack/issues/240
  • https://github.com/webpack/webpack/issues/274
  • https://github.com/webpack-contrib/script-loader/issues/7

Today I found an article by James Kale on medium about his project react-loadable which do almost the same thing as LazilyLoad component but with promise that

  • it supports server-side rendering via a dynamic require()
  • it eagerly preloads components when needed

I highly recommend checking it out.

like image 169
Dawid Karabin Avatar answered Sep 19 '22 21:09

Dawid Karabin