Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

issues with hot reloading #1328

Closed
httnn opened this issue Jun 21, 2017 · 30 comments
Closed

issues with hot reloading #1328

httnn opened this issue Jun 21, 2017 · 30 comments

Comments

@httnn
Copy link

httnn commented Jun 21, 2017

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'
    ]
  }
};
@tmeasday
Copy link
Member

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.

@Memfisrain
Copy link

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.

@onchainguy-btc
Copy link

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.

@onchainguy-btc
Copy link

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')
);

@billyvg
Copy link
Contributor

billyvg commented Jul 20, 2017

@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/

@Memfisrain
Copy link

Memfisrain commented Jul 20, 2017

@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!

@shilman
Copy link
Member

shilman commented Jul 20, 2017

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

@httnn
Copy link
Author

httnn commented Jul 21, 2017

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

@igor-dv
Copy link
Member

igor-dv commented Jul 21, 2017

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

@remotezygote
Copy link

Running into this same issue (details here: #1417 (comment)). I am passing module with every storiesOf call, so that's not the issue for me.

@ariesshrimp
Copy link
Contributor

ariesshrimp commented Aug 8, 2017

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

@thenewvu
Copy link

thenewvu commented Sep 7, 2017

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
}

@ariesshrimp
Copy link
Contributor

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

@ariesshrimp
Copy link
Contributor

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

@ariesshrimp
Copy link
Contributor

ariesshrimp commented Sep 11, 2017

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

@remotezygote
Copy link

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.

@ariesshrimp
Copy link
Contributor

@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?

@ariesshrimp
Copy link
Contributor

@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?

@ariesshrimp
Copy link
Contributor

ariesshrimp commented Sep 21, 2017

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

@thenewvu
Copy link

@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.

@ariesshrimp
Copy link
Contributor

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

@stale
Copy link

stale bot commented Nov 6, 2017

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!

@stale stale bot added the inactive label Nov 6, 2017
@ariesshrimp
Copy link
Contributor

ariesshrimp commented Nov 6, 2017

@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"?

@stale stale bot removed the inactive label Nov 6, 2017
@ngbrown
Copy link

ngbrown commented Nov 7, 2017

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.

@tmeasday
Copy link
Member

tmeasday commented Nov 8, 2017

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!

@tmeasday
Copy link
Member

tmeasday commented Nov 10, 2017

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 #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?)

tmeasday added a commit that referenced this issue Nov 22, 2017
Sometimes when there are problems with HMR, we end up doing a
hard-reload of the preview iframe, whilst leaving the main window.

As the preview iframe didn't change its URL ever, this led to problems
where the old (usually original) story was loaded in such circumstances.

See #614 and #1328
@tmeasday
Copy link
Member

See #2349

@stale
Copy link

stale bot commented Jan 6, 2018

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!

@stale stale bot added the inactive label Jan 6, 2018
@tmeasday
Copy link
Member

tmeasday commented Jan 6, 2018

This was fixed in 3.3.0

@tmeasday tmeasday closed this as completed Jan 6, 2018
@vass-david
Copy link

vass-david commented Mar 24, 2018

Having the same issue with @storybook/react@3.3.1. 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);
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests