Webpack dev server and css modules

1.1k views Asked by At

I can make css modules to work, but not in the hot-reload.

When first loaded, the style appears the way it should:

Class names with css modules

But after making changes to the css file it breaks, and a full reload is needed:

Hot reload not working for css modules

I'm using the css modules as shown below:

css import

webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const pack = {
  entry: [
    path.join(__dirname, 'src/app/index.js'),
  ],
  output: {
    path: path.join(__dirname, '/dist/'),
    filename: '[name].js',
    publicPath: '/',
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/app/index.html',
      inject: 'body',
      filename: 'index.html',
    }),
  ],
  module: {
    loaders: [
      {
        test: /\.css$/,
        include: /src\/app/,
        loaders: [
          'style?sourceMap',
          'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]',
        ],
      },
      {
        test: /\.js?$/,
        include: /src\/app/,
        loader: 'babel',
      },
      {
        test: /\.woff(2)?(\?[a-z0-9#=&.]+)?$/,
        loader: 'url?limit=10000&mimetype=application/font-woff',
      },
      {
        test: /\.(ttf|eot|svg)(\?[a-z0-9#=&.]+)?$/,
        loader: 'file',
      },
    ],
  },
};

module.exports = pack;

webpack.development.config.js

const webpack = require('webpack');

const pack = require('./webpack.config');

pack.entry.unshift('react-hot-loader/patch');
pack.entry.unshift('webpack/hot/only-dev-server');
pack.entry.unshift('webpack-dev-server/client?http://localhost:3000');

pack.plugins.push(new webpack.HotModuleReplacementPlugin());
pack.plugins.push(new webpack.DefinePlugin({
  'process.env.NODE_ENV': JSON.stringify('development'),
}));

module.exports = pack;

As I have noticed, the css class that it tries to fetch in the component stills the same, shouldn't a new hash be generated for each file change/reload?

1

There are 1 answers

0
Filip Dupanović On

The HMR API exposes a small runtime, which is accessed through module.hot, that you'll have to interact with in order to consolidate the updates and apply changes to the document's state, otherwise you're left with something that is akin to a live reload server as you're not opting into HMR.

You have correctly stated yourself that each update should provide with you with a new set of class names, as long as there is a [hash] variant used to construct the name, so what's needed it to invalidate the modules that reference the JS module with the export and re-apply the class names.

There is an example provided in the new documentation, for Webpack 2, which currently has a release candidate available under the beta distribution tag, which you can install via npm i webpack@beta. I would recommend that you migrate over immediately and switch to react-hot-loader 3, available under the next distribution tag, which you can install via npm i react-hot-loader@next, both of which should provide a much better experience. However, you're welcome to use your existing versions.

For HMR to work, you'll need to have a module that requires the root component of your React component tree and render it under RHL's container. This can be performed in a function that has the signature render(root), which you will subsequently call each time one of the modules that is part of your root's module's dependency graph: // ./src/index.js import React from 'react'; import ReactDOM from 'react-dom';

import {AppContainer} from 'react-hot-loader';

import App from './components/app';

const render = (Component) => {
  ReactDOM.render(
    <AppContainer>
      <Component />
    </AppContainer>,
    document.getElementById('root')
  );
};

render(App);

// Hot Module Replacement API
if (module.hot) {
  // Accept updates for all modules that are part of our root app component's
  // module's dependency graph.
  module.hot.accept('./components/app', () => {
    // Re-render, allowing `react-hot-loaders` behavior to kick in,
    // invalidating the whole React element tree.
    render(App)

    // If you are not using native ES6 export syntax, you will have to
    // re-require the export again. 

    // const NewApp = require('./components/app').default
    // render(NewApp)
  });
}