Storybook: issues with hot reloading

Created on 21 Jun 2017  Â·  30Comments  Â·  Source: storybookjs/storybook

we are currently experiencing some issues with hot reloading.

1) changing a file always results in the entire iframe being reloaded (component container goes blank and takes about 3-5s to reload).

2) occasionally changing a file also results in the first component in the sidebar being displayed in the component container, although the sidebar still incorrectly displays the previously active component as still active

I'm not quite sure what could be causing these problems, but we started experiencing these after upgrading to webpack 2 and to storybook 3.1.3 (from 2.35.3)

our config.js looks like this:

import 'babel-polyfill';
import {configure} from '@storybook/react';

import 'sass/main.scss';
import 'react-select/dist/react-select.css';

const componentStories = require.context('../frontend/components', true, /\.story\.js$/);

function loadStories() {
  componentStories.keys().forEach(componentStories);
}

configure(loadStories, module);

and we're also using a custom webpack.config.js with storybook:

var path = require('path');

const sassOptions = {
  includePaths: [
    path.resolve(__dirname, '../frontend/sass'),
    path.resolve(__dirname, '../frontend/img'),
    path.resolve(__dirname, '../src/styles'),
    path.resolve(__dirname, '../frontend/components')
  ].concat(require('bourbon').includePaths)
};

const resolveUrlOptions = {
  sourceMap: true,
  root: path.resolve(__dirname, '../frontend/sass')
};

module.exports = {
  devtool: "eval-source-map",
  module: {
    rules: [
      {
        test: /\.m\.s?css$/,
        use: [
          'style-loader?singleton',
          'css-loader?module&localIdentName=[local]___[hash:base64:5]',
          'postcss-loader',
          {
            loader: 'resolve-url-loader',
            options: resolveUrlOptions
          },
          {
            loader: 'sass-loader',
            options: sassOptions
          }
        ],
        exclude: /(node_modules)/,
        include: path.resolve(__dirname, '../')
      },
      {
        test: /\.s?css$/,
        use: [
          'style-loader?singleton',
          'css-loader',
          'postcss-loader',
          {
            loader: 'resolve-url-loader',
            options: resolveUrlOptions
          },
          {
            loader: 'sass-loader',
            options: Object.assign({}, sassOptions, {sourceMap: true})
          }
        ],
        exclude: /\.m\.scss$/
      },
      {
        test: /\.m\.svg$/,
        use: [
          {
            loader: 'babel-loader'
          },
          {
            loader: 'react-svg-loader',
            query: {
              jsx: '1'
            }
          }
        ]
      },
      {
        test: /\.json$/,
        loader: 'json-loader'
      },
      {
        test: /\.(?:jpe?g|gif|png|wav|mp3)$/,
        loader: 'file-loader?name=[name]-[hash].[ext]'
      },
      {
        test: /\.(ttf|eot|svg|woff|woff2)(\?|$)/,
        loader: 'file-loader?name=[name]-[hash].[ext]',
        exclude: /\.m\.svg$/
      },
      { test: /\.peg$/, loaders: ['babel-loader?cacheDirectory', 'pegjs-loader?cache=true']}
    ]
  },
  resolve: {
    extensions: ['.js', '.jsx'],
    modules: [
      path.resolve(__dirname, '../'),
      path.resolve(__dirname, '../frontend'),
      path.resolve(__dirname, '../src/styles'),
      path.resolve(__dirname, '../frontend/js'),
      'node_modules'
    ]
  }
};

babel / webpack bug inactive

Most helpful comment

Running into this same issue (details here: https://github.com/storybooks/storybook/issues/1417#issuecomment-319439819). I am passing module with every storiesOf call, so that's not the issue for me.

All 30 comments

Thanks for reporting @bodyflex. I wonder if it's possible to reproduce the behaviour consistently on a simple example? That would make it a lot easier to look into.

I have the same issue with iframe reloading and showing the first story in it after reloading despite the fact that in the left navbar another story is active.

Same issue for me. Until this is fixed: Is there anyway to disable hot reloading easily? Tried to remove the HotModuleReplacement plugin from the webpack config but that doesn't work. Probably because the corresponding dev server middleware is still active.

Just in case anybody else finds this useful: Could disable hot reloading by filtering for:

const plugins = storybookBaseConfig.plugins.filter(
  plugin => !(plugin instanceof webpack.HotModuleReplacementPlugin)
);

const preview = storybookBaseConfig.entry.preview.filter(
  previewEntry => !previewEntry.includes('webpack-hot-middleware')
);

@tmeasday Not a simple example, but we have the issue as @Memfisrain. I will try to debug it in the next few weeks, but in case anyone was interested: https://github.com/getsentry/sentry/tree/use-react-storybook
npm run storybook - stories are in docs-ui/

@billyvg @seb0zz @bodyflex @tmeasday , I've found out the problem. In my case it appeared that I skipped the second arguments in storiesOf:

storiesOf('Spinner')
  .add('default', () => <Spinner />)
However, you have to pass **module** as the second arguments:
storiesOf('Spinner',  module)

Unfortunately, there is no warning about this.

I'm going to create a PR, to fill this gap.

Hope, it helps you guys!

Awesome @Memfisrain !! PR to warn about this problem would be very helpful -- I believe you're not the first to run into it.

perfect @Memfisrain, this fixed it for me as well!

@shilman , @UsulPro can it be related to the HMR issue from 3.2 release ?

Running into this same issue (details here: https://github.com/storybooks/storybook/issues/1417#issuecomment-319439819). I am passing module with every storiesOf call, so that's not the issue for me.

So I'm pretty sure that this issue is caused by custom webpack config collisions, but I'm not sure how or what specifically yet. Here's what I've done to reproduce:

npm i -g create-react-app @storybook/cli
create-react-app repro
cd repro && getstorybook
# at this point everything is fine, change some story files, see hmr
yarn run eject
mv config/webpack.config.dev.js .storybook/webpack.config.js
# you'll need to update a few require paths in the webpack config file
# hmr is now broken

I'm guessing that the problem can be uncovered quickly now just by deleting stuff from the .storybook/webpack.config.js file until things work.

Here's a GIF that shows the problem:
Here's a GIF that shows the problem

You can generate your own repro environment using the steps described above, but here's a repo you can clone for convenience: https://github.com/joefraley/repro-storybook-1328

I've experienced the issue that @joefraley mentioned above, the issue has gone after I added the below snippet:

const extend = require('../../web/webpack.config.dev.js')

module.exports = base => {
  base.module.rules = [
    ...base.module.rules,
    ...extend.module.rules
  ]
  base.resolve.alias = {
    ...base.resolve.alias,
    ...extend.resolve.alias,
    '@storybook/react-native': '@storybook/react'
  }
+  base.devServer = {
+    inline: true,
+    hot: true
+  }
  return base
}

@thenewvu i didn't see any change in behavior by switching to full control mode, or by hijacking the devServer configuration

@shilman, @Memfisrain i'm also passing module, as does the simple repro i described above

so i think i got down to the minimum possible reproduction of this bug: https://github.com/joefraley/repro-storybook-1328

// .storybook/webpack.config.js
const webpack = require("webpack");

module.exports = {
  plugins: [new webpack.HotModuleReplacementPlugin()] // <--- 
  // this is the problem, but why?
  // in fact, HotModuleReplacementPlugin is not the only one that seems to bug
};
// .storybook/config.js
import { configure } from "@storybook/react";

configure(() => require("../stories.js"), module);
// stories.js
import React from "react";
import { storiesOf } from "@storybook/react";

import { Button, Welcome } from "@storybook/react/demo";

storiesOf("Welcome", module).add("to Story", () => <Welcome />);

storiesOf("Button", module).add("with text", () => <Button>Hello</Button>);

it's frustrating though because i'm experiencing the same issue in other code bases w/o that plugin

I think maybe this points to the possibly that it's something that's missing rather than something that should not be included. I would expect it not to work without the HMR plugin. Going to scour the storybook-provided config when you go to partial control mode and see what is in that that we're not including. It's possible this is babel config, too.

@remotezygote this also occurs in full-control mode in just the same way. i'm happy to help, storybook is borderline unusable during component development under the weight of this bug.

can you point me in a direction of where to start looking?

@thenewvu can you share or link to the rest of your webpack configuration?

const extend = require('../../web/webpack.config.dev.js') <--- ?

maybe also your storybook entrypoint or a storyfile that was having this problem but now is not?

EDIT this actually doesn't help.

combining some ideas described here, i think i have a workaround for my project:

+  const plugins = base.plugins.filter(
 +    plugin => !(plugin instanceof webpack.HotModuleReplacementPlugin)
 +  );
 +
    base.plugins = [            base.plugins = [
 -    ...base.plugins,       +    ...plugins,
 +    new webpack.HotModuleReplacementPlugin(),
      new webpack.NamedModulesPlugin(),           
      new webpack.NamedModulesPlugin(),
      new ExtractTextPlugin({
        filename: "components.css",
        allChunks: true
      })
    ];

basically i strip hmr plugin from the baseconfig, and i slap it back in from my repo's installed webpack instance, and that seems to work.

i still have no idea what's going on

@joefraley After days experimented that change, I turned out the issue sometimes still happens. Just like you, I still have no idea what's going on.

it seems the issue has something to do with the relationship between the UI url and iframe URL:

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. We do try to do some housekeeping every once in a while so inactive issues will get closed after 60 days. Thanks!

@tmeasday i haven't seen any change in behavior in the latest versions, nor have i seen any solution that works in the minimal-reproduction using create-react-app linked above (and here: https://github.com/joefraley/repro-storybook-1328)

does that qualify the "needs reproduction" tag to be removed from this issue, and replaced with "bug"?

I was having this same issue from the original reporter:

  1. occasionally changing a file also results in the first component in the sidebar being displayed in the component container, although the sidebar still incorrectly displays the previously active component as still active

But when I used Ctrl+Shift+L (from the ⌘ menu) to toggle off the left side-bar menu, it locks onto the page that I want, and does not navigate away when hot-reloading.

does that qualify the "needs reproduction" tag to be removed from this issue, and replaced with "bug"?

Too right! Thanks for the reproduction, very simple!

OK, status report on this one, as I didn't have a lot of time to look at it. Here's the explanation, open to ideas on a fix, I haven't thought about it too much.

The explanation for the behaviour is as follows (at least for @joefraley's repro):

The core issue of course is that we end up with two webpack.HotModuleReplacementPlugins. This is due to the fact that storybook isn't super careful about appending your list of plugins to its list here.

This means what happens when you change a story is::

  • Both the manager (the chrome) and the preview iframe (where the stories render) try to HMR.
  • The manager has a default config and HMRs fine.
  • The preview iframe OTOH uses the extended config and so fails to HMR (due to contention between between the duplicate plugins I suppose), ultimately leading to a window.location.reload() (a "hard-reload").

Because the preview iframe never changes its URL from the initial value that is set when the manager first renders it, it sees the default (initial) story on the URL when it hard-reloads, and because the manager is not hard-reloading, it doesn't think to send a message to the preview to do differently.

I'm pretty sure this is the same issue that causes https://github.com/storybooks/storybook/issues/614 also, although i don't have a working reproduction there right now.

In any case there are two issues to solve:

  1. What to do if users provide HMR in the plugins list? (nothing / warn / strip it / error)?
  2. Making sure that if the preview pane hard-reloads it does something sensible (probably by changing its URL along the way?)

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 60 days. Thanks!

This was fixed in 3.3.0

Having the same issue with @storybook/[email protected]. I'm using custom webpack config. When I add webpack's HMR plugin to this config, it works but in a way that it always reloads a whole iframe even if I only change a styling in my jss stylesheet.

This is my config:

const webpack = require('webpack');
const mergeWebpackConfig = require('webpack-merge');
const { Paths } = require('../webpack/parts');

module.exports = (storybookBaseConfig, configType) => {
  if (configType === 'PRODUCTION') {
    // see https://github.com/storybooks/storybook/issues/1570
    storybookBaseConfig.plugins = storybookBaseConfig.plugins.filter(plugin => plugin.constructor.name !== 'UglifyJsPlugin')
  }

  return mergeWebpackConfig({
    resolve: {
      alias: {
        src: Paths.SRC,
      },
    },
    devtool: 'source-map',

    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: /node_modules/,
          include: [
            /src/,
            /common-components/,
          ],
          loader: 'babel-loader',
          options: {
            presets: [
              'es2015',
              'react',
            ],
            plugins: [
              'transform-class-properties',
              'transform-object-rest-spread',
              'transform-class-properties',
              'syntax-dynamic-import',
            ],
          },
        },
        {
          test: /\.(?:png|jpe?g|otf|gif|svg|woff2?|ttf|eot|ico)$/,
          loader: 'file-loader',
        },
      ]
    },
    plugins: [
      new webpack.HotModuleReplacementPlugin(),
    ],
  }, storybookBaseConfig);
};

Was this page helpful?
0 / 5 - 0 ratings

Related issues

MrOrz picture MrOrz  Â·  3Comments

ZigGreen picture ZigGreen  Â·  3Comments

tomitrescak picture tomitrescak  Â·  3Comments

purplecones picture purplecones  Â·  3Comments

alexanbj picture alexanbj  Â·  3Comments