Storybook: HMR full reload

Created on 5 Jul 2017  路  13Comments  路  Source: storybookjs/storybook

My page seems to do a full reload instead of HMR/live reload. It appears that the config file in indicated as updated when I am working with a component. I assume a small configuration change would fix this. Any insights would be helpful.

[HMR] connected

Change file

[HMR] bundle rebuilding
[HMR] bundle rebuilt in 2253ms
[HMR] Checking for updates on the server...
[HMR] Updated modules:
[HMR]  - ./filepath.js
[HMR]  - ./filepath.js$
[HMR]  - ./storybook/config.js
[HMR] App is up to date.

Relevant package.json snippet

{
  "@storybook/addon-actions": "^3.1.6",
  "@storybook/addon-links": "^3.1.6",
  "@storybook/addon-options": "^3.1.6",
  "@storybook/addons": "^3.1.6",
  "@storybook/react": "^3.1.7"
}

The custom storybook config includes the following line:

configure(() => req.keys().forEach(req), module);

It loads in the stories, so that may be part of the issue.

babel / webpack compatibility with other tools has workaround help wanted needs more info needs reproduction question / support

Most helpful comment

FWIW, I have actually now fixed this issue. The fix was to put a module.hot.accept at the entry of our story loader file, and I just have it reload the stories at that point. Works well, as far as I can tell... I'll just dump our whole loader script here, in case it helps others (we use lerna, hence the packages stuff):

require('babel-register');
import configureStorybook from './configurator';
import '../src';

__webpack_public_path__ = 'http://localhost:9000/';

function loadStory(loader, key) {
  if (!/node_modules/.test(key)) {
    performance.mark(`start-${key}`);
    loader(key);
    performance.mark(`end-${key}`);
    performance.measure(key, `start-${key}`, `end-${key}`);
  }
}

function loadStories() {
  const inSrc = require.context('../packages', true, /\bstories\.js$/);
  inSrc.keys().forEach(loadStory.bind(this, inSrc));
  const stories = require.context('../stories', false, /.js$/);
  stories.keys().forEach(loadStory.bind(this, stories));
  console.table(performance.getEntriesByType("measure"), ['name', 'duration']);
  performance.clearMarks();
  performance.clearMeasures();
}

configureStorybook(loadStories);

if (module.hot) {
  module.hot.accept(() => configureStorybook(loadStories));
}

All 13 comments

That's weird, it should not do that.. something's odd about your setup, possibly you config or the components or something in between.

Can you share more details and code samples?

I am having the same issue, and am having trouble reproducing it in a smaller test case that I can share. I am using full control mode with the webpack config, and have a custom .babelrc, and have fiddled and messed with every single thing I can imagine, and nothing seems to work. I am using the env preset for babel - possibly that is the problem? Not sure what next steps are here.

Here's the best JSON dump I can do for our full webpack config, with paths and names redacted:

{
  "devtool": "cheap-module-source-map",
  "entry": {
    "manager": [
      "/Users/<redacted>/workspaces/<redacted>/node_modules/@storybook/react/dist/server/addons.js",
      "/Users/<redacted>/workspaces/<redacted>/node_modules/@storybook/react/dist/server/config/polyfills.js",
      "/Users/<redacted>/workspaces/<redacted>/node_modules/@storybook/react/dist/client/manager/index.js"
    ],
    "preview": [
      "/Users/<redacted>/workspaces/<redacted>/node_modules/@storybook/react/dist/server/config/polyfills.js",
      "/Users/<redacted>/workspaces/<redacted>/node_modules/@storybook/react/dist/server/config/globals.js",
      "/Users/<redacted>/workspaces/<redacted>/node_modules/webpack-hot-middleware/client.js?reload=true",
      "/Users/<redacted>/workspaces/<redacted>/.storybook/config.js"
    ]
  },
  "externals": [
    null
  ],
  "module": {
    "rules": [
      {
        "test": {},
        "loader": "/Users/<redacted>/workspaces/<redacted>/node_modules/babel-loader/lib/index.js",
        "query": {
          "cacheDirectory": "/Users/<redacted>/workspaces/<redacted>/node_modules/.cache/react-storybook",
          "presets": [
            [
              "env",
              {
                "loose": true,
                "modules": "umd",
                "targets": {
                  "uglify": false,
                  "browsers": [
                    "last 2 versions",
                    "ie >= 7"
                  ]
                }
              }
            ],
            "react"
          ],
          "plugins": [
            "transform-export-extensions",
            "transform-class-properties",
            "transform-decorators-legacy",
            "transform-object-rest-spread",
            [
              "/Users/<redacted>/workspaces/<redacted>/node_modules/babel-plugin-react-docgen/lib/index.js",
              {
                "DOC_GEN_COLLECTION_NAME": "STORYBOOK_REACT_CLASSES"
              }
            ]
          ],
          "env": {
            "test": {
              "plugins": [
                "istanbul"
              ],
              "presets": [
                [
                  "env",
                  {
                    "loose": true,
                    "modules": "umd",
                    "targets": {
                      "node": true
                    }
                  }
                ],
                "react"
              ]
            }
          },
          "sourceMaps": "both",
          "babelrc": false
        },
        "include": [
          "/Users/<redacted>/workspaces/<redacted>"
        ],
        "exclude": [
          "/Users/<redacted>/workspaces/<redacted>/node_modules"
        ]
      },
      {
        "test": {},
        "use": [
          {
            "loader": "style-loader",
            "options": {
              "sourceMap": true,
              "singleton": true
            }
          },
          {
            "loader": "css-loader",
            "options": {
              "sourceMap": true
            }
          },
          {
            "loader": "postcss-loader",
            "options": {
              "sourceMap": true
            }
          },
          {
            "loader": "fast-sass-loader",
            "options": {
              "sourceMap": true,
              "importer": [
                null
              ],
              "includePaths": [
                "/Users/<redacted>/workspaces/<redacted>/packages",
                "/Users/<redacted>/workspaces/<redacted>/node_modules",
                "/Users/<redacted>",
                "/node_modules"
              ]
            }
          }
        ],
        "exclude": {}
      },
      {
        "test": {},
        "use": [
          "svg-sprite-loader?{\"name\":\"<redacted>-icon-[name]\"}",
          {
            "loader": "svgo-loader",
            "options": {
              "plugins": [
                {
                  "mergePaths": true
                },
                {
                  "removeTitle": true
                },
                {
                  "removeDesc": true
                },
                {
                  "removeXMLNS": true
                },
                {
                  "transformsWithOnePath": true
                },
                {
                  "removeDimensions": true
                },
                {
                  "removeAttrsWithValue": {
                    "type": "perItem",
                    "description": "Remove attribute/value pairs on match.",
                    "params": {
                      "attrs": [
                        [
                          "fill",
                          "<redacted>"
                        ],
                        [
                          "fill",
                          "<redacted>"
                        ]
                      ]
                    }
                  }
                }
              ]
            }
          }
        ]
      },
      {
        "test": {},
        "loaders": [
          {
            "loader": "style-loader",
            "options": {
              "sourceMap": true,
              "singleton": true
            }
          },
          {
            "loader": "css-loader",
            "options": {
              "sourceMap": true
            }
          },
          {
            "loader": "postcss-loader",
            "options": {
              "sourceMap": true
            }
          }
        ]
      },
      {
        "test": {},
        "loader": "json-loader"
      },
      {
        "test": {},
        "loader": "expose?WaveSurfer"
      }
    ]
  },
  "output": {
    "path": "/Users/<redacted>/workspaces/<redacted>/node_modules/@storybook/react/dist/server/config/dist",
    "filename": "static/[name].bundle.js",
    "publicPath": "/"
  },
  "plugins": [
    {
      "preferEntry": true
    },
    {
      "definitions": {
        "process.env": {
          "NODE_ENV": "\"development\""
        }
      }
    },
    {
      "options": {
        "options": {
          "context": "/Users/<redacted>/workspaces/<redacted>/src",
          "output": {
            "path": "/Users/<redacted>/workspaces/<redacted>/dist"
          }
        },
        "test": {}
      }
    },
    {
      "definitions": {
        "process.env": {
          "NODE_ENV": "\"development\"",
          "PUBLIC_URL": "\"\"",
          "STORYBOOK_GIT_ORIGIN": "\"[email protected]:<redacted>.git\"",
          "STORYBOOK_GIT_BRANCH": "\"<redacted>\""
        }
      }
    },
    {
      "fullBuildTimeout": 200
    },
    {
      "options": {},
      "pathCache": {},
      "fsOperations": 0,
      "primed": false
    },
    {
      "nodeModulesPath": "/Users/<redacted>/workspaces/<redacted>/node_modules"
    },
    {}
  ],
  "resolve": {
    "modules": [
      "node_modules",
      "/Users/<redacted>/workspaces/<redacted>",
      "/Users/<redacted>/workspaces/<redacted>/src",
      "/Users/<redacted>/workspaces/<redacted>/node_modules",
      "/Users/<redacted>",
      "/Users/<redacted>/node_modules",
      "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src",
      "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src",
      "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src",
      "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src",
      "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src",
      "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src",
      "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src",
      "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src",
      "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src",
      "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src",
      "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src",
      "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src",
      "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src"
    ],
    "alias": {
      "<redacted>": "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src/index.js",
      "<redacted>": "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src/index.js",
      "<redacted>": "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>",
      "<redacted>": "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src/index.js",
      "<redacted>": "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src/index.js",
      "<redacted>": "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src/index.js",
      "<redacted>": "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>",
      "<redacted>": "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>",
      "<redacted>": "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src/index.js",
      "<redacted>": "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src/index.js",
      "<redacted>": "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src/_main.scss",
      "<redacted>": "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src/index.js",
      "<redacted>": "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>",
      "<redacted>": "/Users/<redacted>/workspaces/<redacted>/packages/<redacted>/src/index.js"
    }
  },
  "performance": {
    "hints": false
  }
}

and .babelrc:

{
  "presets": [
    [
      "env",
      {
        "loose": true,
        "modules": "umd",
        "targets": {
          "uglify": false,
          "browsers": [
            "last 2 versions",
            "ie >= 7"
          ]
        }
      }
    ],
    "react"
  ],
  "plugins": [
    "transform-export-extensions",
    "transform-class-properties",
    "transform-decorators-legacy",
    "transform-object-rest-spread"
  ],
  "env": {
    "test": {
      "plugins": [
        "istanbul"
      ],
      "presets": [
        [
          "env",
          {
            "loose": true,
            "modules": "umd",
            "targets": {
              "node": true
            }
          }
        ],
        "react"
      ]
    }
  },
  "sourceMaps": "both"
}

When I update a file, I get the following messages:

[HMR] The following modules couldn't be hot updated: (Full reload needed)
This is usually because the modules which have changed (and their parents) do not know how to hot reload themselves. See http://webpack.github.io/docs/hot-module-replacement-with-webpack.html for more details.

[HMR]  - ./packages/<redacted>/src/<redacted>/index.js

[HMR] Reloading page

I realize this is a very complicated setup. It has been working great for us until recently. I have not been able to determine why it thinks none of our components know how to hot reload. Any thoughts on things to try?

I've looked through your configs and nothing spots my eye of what could be causing it.

Are you passing the module instance to the storiesOf()-calls?

storiesOf('The best component', module)
  .add('in the best state', () => {
    // ...
  });

Yup, we're passing module to each and every call.

I am seeing this as well, I think it may be due to passing '?reload=true' as an argument webpack-hot-middleware? Not sure why that is being done?

see https://github.com/storybooks/storybook/blob/b7a90fdd45a7c23fff5ad3db2b910595405daa53/app/react/src/server/config/webpack.config.js#L16

@frankwallis i experience the issue even after manually removing ?reload=true from that line

@frankwallis @joefraley Can confirm. Removing that flag appears to make no difference.

I think we accidentally fixed it on our install by removing addDecorator

@kirkstrobeck do you mean using only add everywhere that you were using addDecorator?

Hey @joefraley

  • We were using addDecorator and now we鈥檙e not
  • We didn鈥檛 replace it with add

I hope that addresses your question, not sure if it does though.

FWIW, I have actually now fixed this issue. The fix was to put a module.hot.accept at the entry of our story loader file, and I just have it reload the stories at that point. Works well, as far as I can tell... I'll just dump our whole loader script here, in case it helps others (we use lerna, hence the packages stuff):

require('babel-register');
import configureStorybook from './configurator';
import '../src';

__webpack_public_path__ = 'http://localhost:9000/';

function loadStory(loader, key) {
  if (!/node_modules/.test(key)) {
    performance.mark(`start-${key}`);
    loader(key);
    performance.mark(`end-${key}`);
    performance.measure(key, `start-${key}`, `end-${key}`);
  }
}

function loadStories() {
  const inSrc = require.context('../packages', true, /\bstories\.js$/);
  inSrc.keys().forEach(loadStory.bind(this, inSrc));
  const stories = require.context('../stories', false, /.js$/);
  stories.keys().forEach(loadStory.bind(this, stories));
  console.table(performance.getEntriesByType("measure"), ['name', 'duration']);
  performance.clearMarks();
  performance.clearMeasures();
}

configureStorybook(loadStories);

if (module.hot) {
  module.hot.accept(() => configureStorybook(loadStories));
}

Hi! I have the same problem, but on Angular. When I change something slightly, the input fields of my simple component get cleared, and the whole state is lost.
And the ./storybook/config.js file gets indicated as updated.
@remotezygote 's fix didn't help.
Any suggestions? Is it as expected?

const req = require.context("../lib", true, /.stories.js$/);

function loadStories() {
req.keys().forEach(filename => req(filename));
}

configure(loadStories, module);

if (module.hot) {
module.hot.accept(() => configure(loadStories, module));
}

Was this page helpful?
0 / 5 - 0 ratings