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

3.0.0-beta.6: Component gets mounted twice #457

Closed
ianks opened this issue Jan 6, 2017 · 13 comments
Closed

3.0.0-beta.6: Component gets mounted twice #457

ianks opened this issue Jan 6, 2017 · 13 comments
Labels

Comments

@ianks
Copy link

ianks commented Jan 6, 2017

Description

When RHL3, I am seeing this warning when files are updated:

Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the OverviewHeader component.

This is happening because I have a function which calls setState in componentDidMount. However, the component's componentDidMount is called twice; meaning that setState can be called when a component is unmounted.

Expected behavior

I would expect componentDidMount to be called once after hot reloading.

Actual behavior

``componentDidMount` is called twice.

Environment

React Hot Loader version:

Run these commands in the project folder and fill in their results:

  1. node -v: v6.9.1

Then, specify:

  1. Operating system: Linux 4.8.15-2
  2. Browser and version: Chromium 55.0.2883.87

Reproducible Demo

The project I am on is huge; and creating a reproducible demo would take a couple hours. So here are my config options. If this proves unhelpful, I will spend more time to try and create one. My goal is to see if anyone else has experienced this issue, and if anyone has fixed this before.

Things to note:

  1. Not using babel.
  2. It is a typescript project.
'use strict';

const _                 = require('lodash');
const baseConfig        = require('./base');
const path              = require('path');
const webpack           = require('webpack');
const SassLintPlugin    = require('sasslint-webpack-plugin');

const host = '0.0.0.0';
const port = 8000;

const config = _.assign(baseConfig, {
  entry: {
    app: [
      'react-hot-loader/patch',
      'webpack-dev-server/client?http://local.tryadhawk.com:8000/',
      'webpack/hot/only-dev-server',
      './src/components/RunThePower.tsx',
    ],
  },
  output: {
    path: path.resolve(__dirname, '..', '..', 'dist', 'the-power', 'assets'),
    filename: '[name].js',
    publicPath: 'http://' + host + ':' + port + '/assets/',
  },
  cache: true,
  devtool: 'cheap-module-inline-source-map',
  devServer: {
    contentBase: path.resolve(__dirname, '..', '..', 'src'),
  },
  plugins: [
    ...baseConfig.plugins,
    new SassLintPlugin({
      context: './src/styles/',
      configFile: path.resolve(__dirname, '..', 'sass-lint.yml'),
      ignoreFiles: [
        './src/styles/signin/SigninForm.scss',
        './src/styles/App.scss',
        './src/styles/tips/TipsEmptyState.scss',
        './src/styles/dashboard/DateRangePicker.scss',
        './src/styles/LoadingScreen/LoadingScreenComponent.scss',
      ],
    }),
    new webpack.ProvidePlugin({
      Promise: 'imports-loader?this=>global!exports-loader?global.Promise!bluebird',
    }),
    new webpack.DllReferencePlugin({
      context: path.resolve(__dirname, '..', '..'),
      manifest: require(path.resolve(__dirname, '..', '..', '.tmp', 'dll', 'vendor-manifest.json')),
    }),
    new webpack.DefinePlugin({
      __DEV__: true,
      __SLINGSHOT_DEV__: process.env.SLINGSHOT_DEV ? true : false,
      'process.env': { NODE_ENV: JSON.stringify('development') },
    }),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin(),
    new webpack.NamedModulesPlugin()
  ],
});

config.module.loaders = baseConfig.module.loaders.concat([
  {
    test: /\.tsx$/,
    loaders: ['react-hot-loader/webpack', 'awesome-typescript-loader'],
    include: path.resolve(__dirname, '..', '..', 'src'),
  },
  {
    test: /\.ts$/,
    loaders: ['awesome-typescript-loader'],
    include: path.resolve(__dirname, '..', '..', 'src'),
  },
  {
    test: /\.css$/,
    loaders: ['style-loader', 'css-loader'],
  },
  {
    test: /\.scss$/,
    loaders: ['style-loader', 'css-loader?sourceMap', 'sass-loader?sourceMap'],
  },
]);

module.exports = config;
ReactDOM.render(
  <AppContainer>
    <Root />
  </AppContainer>,
  document.getElementById('app')
);

declare const module: any;
if (module.hot) {
  module.hot.accept('./../containers/Root', () => {
    const NextApp = require('./../containers/Root').default;

    ReactDOM.render(
      <AppContainer>
        <NextApp />
      </AppContainer>,
      document.getElementById('app')
    );
  });
}
@Kimel1
Copy link

Kimel1 commented Jan 16, 2017

Same problem
Win10 x64

@mutsys
Copy link

mutsys commented Jan 19, 2017

I just ran into this same problem today as well, but I don't think it is actually the fault of RHL. It only seems to occur when RHL tries to update a module and is unable to do so. It does produce a warning stating something to that affect. Once that happens, calls to methods of React.Component start to produce errors similar to those reported by @ianks. If I perform a full refresh of the browser, forcing a proper update of the modules reporting the errors, the errors go away and everything then works as expected.

The follow up then is, what would prevent RHL from being able to being able to update a module and what should I be doing in order to make sure my components don't choke RHL?

More info: upon closer inspection, the issue is limited to methods called on a ref that is a pointer to a component that has been unmounted by RHL. It appears that assigning a ref to a variable results in a pointer to the original component before it has been wrapped by RHL. After an update, the variable now points to an unmounted component and calling any of its methods that could result in a re-rendering produce an error message and the expected re-rendering does not take place. Here is a sample error:

Warning: forceUpdate(...): Can only update a mounted or mounting component. This usually means you called forceUpdate() on an unmounted component. This is a no-op. Please check the code for the ImpulseGraph component.

@mutsys
Copy link

mutsys commented Jan 19, 2017

@ianks : this is not a problem with RHL, you are stepping on your own feet here and causing this to happen. The docs for componentDidMount() :

Setting state in this method will trigger a re-rendering.

https://facebook.github.io/react/docs/react-component.html#componentdidmount

Move any setState() that you are doing in componentDidMount() to the constructor of your component class and that should make your problem go away.

BTW, you probably want to review your webpack config. It seems to contain a number of errors as well.

@ianks
Copy link
Author

ianks commented Jan 20, 2017

Move any setState() that you are doing in componentDidMount() to the constructor of your component class and that should make your problem go away.

This will cause another re-rendering (which is fine), it should not trigger a re-mount.

@bradennapier
Copy link
Contributor

I am having a similar issue but dont do setState in the component. When I have the 'react-hot-loader/babel' preset set any of my onMouseEnters that trigger a setState will create this error. Commenting it out fixes it .

@EvHaus
Copy link

EvHaus commented May 5, 2017

I just run into the same Can only update a mounted or mounting component. warning but I'm not doing any setState calls in componentDidMount. For me the error occurred after I removed transform-es2015-classes from my Babel plugins (so that I'm not outputting full ES6 classes).

The error was caused by the fact that I still had react-hot-loader/babel in my Babel configuration which appears to be only needed if you're transpiling ES2015 classes. After I removed react-hot-loader/babel from my Babel config -- the error went away.

Not sure if this is the same problem as you're having -- but thought I'd share in case others run into this.

@impaler
Copy link

impaler commented May 6, 2017

I also go the setState error with webpack & typescript. I noticed removing the "react-hot-loader/patch" from my webpack entry stopped the error.

If your using typescript and changing the tsconfig.json compilerOptions -> target to es5 seems to workaround the error aswell.

@AvailCat
Copy link

Same issue, move setState to constructor did not work

@mutsys
Copy link

mutsys commented May 17, 2017

@Meeeeow You should not be calling setState in a constructor. Instead, you should just set a value for the state property in the usual way:

export class MyComponent extends React.Component<MyComponentProps, MyComponentState> {

    constructor(props: MyComponentProps) {
        super(props);
        this.state = {
            // whatever is required for your component's state
        };
    }

}

@AvailCat
Copy link

@mutsys
You says:

Move any setState() that you are doing in componentDidMount() to the constructor of your component class and that should make your problem go away.

My component will send XHR request to load data and set loading status in state, i should use this.state = {} in constructor or place setState to somewhere else?

@smilkobuta
Copy link

@impaler Thank you! I was in a same situation but solved by removing the "react-hot-loader/patch" from entry.

I also go the setState error with webpack & typescript. I noticed removing the "react-hot-loader/patch" from my webpack entry stopped the error.

If your using typescript and changing the tsconfig.json compilerOptions -> target to es5 seems to workaround the error aswell.

@penx
Copy link

penx commented Sep 12, 2017

I had this issue when trying to switch from babel-preset-es2015 to babel-preset-env. Adding transform-es2015-classes to the included transforms resolved it for me:

presets: [
  ['env', {
    targets: {
      chrome: 60
    },
    include: ['transform-es2015-classes']
  }],
  'stage-1',
  'react'
],
plugins: ['react-hot-loader/babel'] 

@gregberge
Copy link
Collaborator

This is completely solved in React Hot Loader v4.

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

No branches or pull requests