gaearon / gaearon/react-hot-loader

Enable every async "loader" to work with RHL

Open
#1,027 2 comments 4 reactions 0 assignees View on GitHub
discussion enhancement
Dominant language
JavaScript
Stars
12.2k
Forks
775
PR merge metrics
No merged PRs in 30d

Description

RHL and HRM comes together, and HRM fails to reload data - RHL also could not work.

All modern loaders has some assumptions about how to detect HMR:
- imported-component - based on PureComponentUpdate (ie someone (RHL) force updated it)
- universal-component - based on componentWillRecieveProps (lots of false positive)
- loadable-component - just always loads component in dev mode ("deferred" loading is not a thing)
- react-loadable - no assumptions

Meanwhile - we could execute the same trick `loadable-component` uses - just import new file, it will `register` itself, and next RHL will perform an actual "update".

The idea behind is based on how "module system" works - when you are importing something, you are becoming a parent, for a module you did import. As a parent - you will get HRM events, you may react on.

I could see 2 ways

1. Add `module.accept` using babel plugin
```js
const Component = Loadable({
loader: () => import('./module')
});

^^^^^^^^^^^^^^^^^^^^^
// then be ready to reload all modules, with ReactComponents registered and used.
// not _all_ modules and _all_ imports, only those ones, who could work using RHL.register to update instance.
module.hot.accept(
''./module',
() => reactHotLoader.hasComponentsFrom('./module') && import('./module')
);
```
1.1 - we might use `statusHandler` instead of `accept` to observe, not to change the code. The same trick we are using in `hot` HOC.

2. Create a separated imported file, as `react-imported-component` does, then track all loaded files, and reload them on HRM.
The major benefit of this way - it creates __another__ module, and `accept` in that file will work in __parallel__, not affecting the "real code", ie the cases when you might have "accept" in another place. Probably this is not the case keeping in mind 1.1
```js
const Component = Loadable({
loader: () => hotRelodable('./module', import('./module'))
});
/////
# hot-relodable.js
// all the "imports" of project, extracted by plugin
const hotRelodables = {
'./module': () => import('./module')
};

const hotRelodable = (moduleName, promise) => {
hotRelodables[moduleName]();
return promise;
}
module.hot.accept(reloadAllLoadedRelodablesWhichAreComponents)
```

- Second way is a bit more hard to maintain, probably we should go with .1
- The only thing we need to impliment - internal tracking of factual usage of some component from some file.
- RHL version 1 did something similar, reloading everything by it's own, but _everything_. I hope that not accepting all possible changes, but only "component-reloading" ones could do things better.

CC #953

Contributor guide

Open the contributing guide

Research direction

Start by reading the existing `hot` HOC HMR handling and the async-loader approaches named in the issue, including `loadable-component` and `react-imported-component`. Compare the proposed Babel `module.hot.accept` approach with the separate tracking module, then define which approach can track component usage and reload only affected components; the issue does not name tests or implementation files.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, react
Domain
frontend, tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.