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

Async component won't hot-reload #14

Closed
plhosk opened this issue Feb 3, 2017 · 18 comments
Closed

Async component won't hot-reload #14

plhosk opened this issue Feb 3, 2017 · 18 comments

Comments

@plhosk
Copy link

plhosk commented Feb 3, 2017

I moved my app from code-split-component to react-async-component but the Async components don't seem to hot reload properly. When I make a change to an Async component I get the usual messages in Chrome console with no differences ([HMR].. ) however the displayed content doesn't change. If I change an Async component then also change a non-async component, the changes to both appear at the same time.

Relevant section of App.jsx:

import { withAsyncComponents } from 'react-async-component'

import store from './store'
import Root from './Root'

function renderApp(Param) {
  const app = (
    <AppContainer>
      <Provider store={store}>
        <Param />
      </Provider>
    </AppContainer>
  )

  withAsyncComponents(app).then(({ appWithAsyncComponents }) =>
    ReactDOM.render(appWithAsyncComponents, rootElement), // eslint-disable-line
  )
}

renderApp(Root)

if (module.hot) {
  module.hot.accept(
    './Root',
    () => renderApp(require('./Root').default), //eslint-disable-line
  )
}

AppContent.jsx:

import IntroductionAsync from './IntroductionAsync'

<Match pattern="/" exactly component={IntroductionAsync} />

IntroductionAsync.jsx:

import { createAsyncComponent } from 'react-async-component'

const AsyncIntroduction = createAsyncComponent({
  resolve: () => System.import('./Introduction'),
})

export default AsyncIntroduction

The problem occurs when I try to edit Introduction.jsx.

[email protected]
[email protected]
[email protected]

Is there something I'm doing wrong or is this a bug of some sort?

@plhosk
Copy link
Author

plhosk commented Feb 4, 2017

As a temporary workaround it started working when I replaced

      <Match pattern="/" exactly component={IntroductionAsync} />

with the following:

      <Match
        pattern="/"
        exactly
        render={() => {
          const AsyncComponent = createAsyncComponent({
            resolve: () => System.import('./Introduction'),
          })
          return <AsyncComponent />
        }}
      />

(EDIT: This workaround is not recommended and will cause rendering issues)

Any idea why it wasn't working when I had the code for createAsyncComponent in its own file?

@carsonxyz
Copy link

Experiencing the same issue myself.

@swernerx
Copy link

swernerx commented Feb 6, 2017

Ooops. The fix would be problematic as it recreates the binding for each render. I don't think that would be a good idea.

@carsonxyz
Copy link

@swernerx I noticed that as well when I tried it and saw that the component was being lazy loaded again and again.

@ctrlplusb could this be a bug or an implementation issue?

@ctrlplusb
Copy link
Owner

ctrlplusb commented Feb 6, 2017

Woops, replied on the wrong thread.

To be honest I expect RHL will give us issues but so far my personal experience has been okay. It would be good to document where and when the hot reloading stops.

Falling back to standard webpack hot reloading is more reliable, but you sacrifice state maintenance.

@ctrlplusb
Copy link
Owner

Yep, agreed with @swernerx - that workaround is not recommended. Your component will always be lazy loaded which could lead to render flashing.

@plhosk
Copy link
Author

plhosk commented Feb 6, 2017

Thanks, I edited my comment with a warning. Hopefully a real solution can be found.

@bradbarrow
Copy link

Hi folks, I'm experiencing the same.

I'm working in a react-universally repo from a month or so ago.

I just updated from code-split-component and left most of the rest of the app alone. I've followed the approach recommended in the README but I have the same issue as above - HMR logs in console with no actual updates.

@wmertens
Copy link

There was an issue with HMR 3 but that has been fixed now gaearon/react-hot-loader#303 - is that what you are seeing?

@ctrlplusb
Copy link
Owner

All, I am almost done with 1.0.0-alpha.2, which includes significant improvements towards supporting hot reloading, including React Hot Loader. So far it is working swimmingly, but I need to put it through some of my larger projects to know for sure.

This should land within a few days.

@GuillaumeCisco
Copy link

GuillaumeCisco commented Mar 28, 2017

It works great for me with react-hot-loader and current or alpha version of react-async-component.
I use this piece of code:

import 'babel-core/register';
import 'babel-polyfill';

import FastClick from 'fastclick';
import React from 'react';
import {render} from 'react-dom';
import {createRenderer} from 'fela';
import {Provider} from 'react-fela';
import {AsyncComponentProvider} from 'react-async-component';
import injectTapEventPlugin from 'react-tap-event-plugin';
import {AppContainer} from 'react-hot-loader';
import Root from './app/Root/index';
import configureStore from './app/configureStore/index';

const store = configureStore();

FastClick.attach(document.body);
// Needed for onTouchTap
// http://stackoverflow.com/a/34015469/988941
injectTapEventPlugin();

const root = document.getElementById('root');
const renderer = createRenderer();
const mountNode = document.getElementById('stylesheet');
const app = <AsyncComponentProvider>
    <Provider renderer={renderer} mountNode={mountNode}>
        <Root {...{store}} />
    </Provider>
</AsyncComponentProvider>;

const renderHotApp = (RootElement) => {
    render(<AppContainer key={Math.random()}>
        <AsyncComponentProvider>
            <Provider renderer={renderer} mountNode={mountNode}>
                <RootElement {...{store}} />
            </Provider>
        </AsyncComponentProvider>
    </AppContainer>, root);
};


if (process.env.NODE_ENV !== 'production') {
    if (module.hot) {
        module.hot.accept('./app/Root/index', () =>
            System.import('./app/Root/index').then(module =>
                renderHotApp(module.default),
            ),
        );
    }
    renderHotApp(Root);
}
else {
    render(app, root);
}

Note that I use:

<AppContainer key={Math.random()}>

Otherwise, the async-ed components are not loaded.
I use react-router v4 and react-fela :)

@ctrlplusb
Copy link
Owner

Hey all;

React hot loader works with alpha-2 release 🎉

I'll be updating the next branch of react-universally soon.

@GuillaumeCisco
Copy link

Hey @ctrlplusb I've just tested the alpha-2 release, it is the same for my settings.
I still need to put:

<AppContainer key={Math.random()}>

Is this normal?

@ctrlplusb
Copy link
Owner

Hmmm, no that shouldn't be the case, but RHL can be very fidgety to get working, and there are definitely some cases where it won't work at all.

Take a look at my starter kit for an example of how I set it up. I have it hot reloading in there pretty well. 👍

@GuillaumeCisco
Copy link

GuillaumeCisco commented Apr 11, 2017

Thank you @ctrlplusb
I've just looked at your setup and tried to reproduce it but I have the same issue.
I need module.hot.accept and key={Math.random()}

But I used one of your file which is very clever ReactHotLoader.js -> https://github.com/ctrlplusb/react-universally/blob/d71b1e4475b31155260ea7ad3d793890aef5aed4/client/components/ReactHotLoader.js

My new setup is now simplier:

/* globals document */

import 'babel-core/register';
import 'babel-polyfill';

import FastClick from 'fastclick';
import React from 'react';
import {render} from 'react-dom';
import {createRenderer} from 'fela';
import {Provider as FelaProvider} from 'react-fela';
import {AsyncComponentProvider} from 'react-async-component';
import injectTapEventPlugin from 'react-tap-event-plugin';
import ReactHotLoader from './ReactHotLoader';
import Root from './app/Root/index';
import configureStore from './app/configureStore/index';

const store = configureStore();

FastClick.attach(document.body);
// Needed for onTouchTap
// http://stackoverflow.com/a/34015469/988941
injectTapEventPlugin();

const root = document.getElementById('root');
const renderer = createRenderer();
const mountNode = document.getElementById('stylesheet');

const renderApp = RootElement => {
    const app = <ReactHotLoader key={Math.random()}>
        <AsyncComponentProvider>
            <FelaProvider renderer={renderer} mountNode={mountNode}>
                <RootElement {...{store}} />
            </FelaProvider>
        </AsyncComponentProvider>
    </ReactHotLoader>;

    render(app, root);
};

if (process.env.NODE_ENV !== 'production' && module.hot) {
    module.hot.accept('./app/Root/index', () =>
        System.import('./app/Root/index').then(module => renderApp(module.default)),
    );
}

renderApp(Root);

My RootElement includes the Router because it is different when I'm in dev or prod environment ;)

@gooddaddy
Copy link

gooddaddy commented Nov 12, 2017

    "react-async-bootstrapper": "^1.1.2",
    "react-async-component": "^1.0.2",
   "react": "^16.0.0",
    "webpack": "^3.8.1",
    "webpack-dev-middleware": "^1.12.0",
    "webpack-hot-middleware": "^2.20.0",

I still need to put:
<AppContainer key={Math.random()}>,
If I do not, it will not work properly, always second times to take effect(如果不这样做,它会不正常工作,总是第二次才生效)。

@rhyek
Copy link

rhyek commented Dec 28, 2017

I also need to use <AppContainer key={Math.random()}>, but I've noticed reloading works fine without it for components not connected to redux. Connected components only reload for every other change.

@rhyek
Copy link

rhyek commented Dec 29, 2017

Fyi, connected components only reloading every other change is no longer an issue with react-hot-loader 4 beta 7.

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

No branches or pull requests

9 participants