-
Notifications
You must be signed in to change notification settings - Fork 143
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
What about react HMR #228
Comments
Hmm, this is interesting. At my work, we use react-hot-loader 1.x, which I believe didn't have a Babel plugin. Even with the newest version, my reading of things is that the Babel plugin helps with some edge cases, but isn't necessary for most hot reloading. (I haven't tried it yet, though.) I'll take a closer look at this when I get a chance. I agree that hot reloading is important enough that Sucrase should have some answer to it. I'm certainly not opposed to implementing it directly in Sucrase, although I worry about the implementation being really difficult, depending on what the Babel plugin really needs to do. |
The current version of react-hot-loader uses an HOC proxy component which I think should be usable with sucrase with a some tweaking. |
The new react-hot-loader@4 uses babel with typescript. We're using it with webpack4 and ts-loader, but didn't test it with sucrase yet, as we're blocked with #225. Anyway the babel step will slow things down, so direct sucrase support would be great.
|
G'Day from React-Hot-Loader.
It should be super hard to create a sucrase plugin to do the same. |
Thanks for the details!
Yeah, maybe in the future there will be a reasonable plugin API, but for now I'm pretty hesitant about that. Sucrase's primary goal is to be fast, and to do that, it drops a bunch of parser features (keeping only what the transforms really need) and does a single replacement pass through the code, which is less composable than Babel's approach. In practice, adding new transforms has required me to make changes to the transform structure so that they cooperate well. Babel's parser is similarly non-extensible: https://new.babeljs.io/docs/en/next/babel-parser.html#faq react-hot-loader is popular enough that I think it's fine for the transform to be built-in. (For less popular things, I think the best approach would be a fork.) I suppose that makes it harder to update the transform, though, so maybe there's some partial plugin API that would allow basic details of the react-hot-loader transform to be updated independently. I looked at some examples and read the code for the Babel plugin. Here's an example: import React from 'react';
const x = 3;
function blah() {
}
class Foo extends React.Component {
_handleSomething = () => {
return 3;
};
render() {
return <span onChange={this._handleSomething} />;
}
}
class SomeOtherClass {
test() {}
}
export default 12; becomes: (function () {
var enterModule = require('react-hot-loader').enterModule;
enterModule && enterModule(module);
})();
import React from 'react';
const x = 3;
function blah() {}
class Foo extends React.Component {
_handleSomething = () => {
return 3;
};
render() {
return <span onChange={this._handleSomething} />;
}
// @ts-ignore
__reactstandin__regenerateByEval(key, code) {
// @ts-ignore
this[key] = eval(code);
}
}
class SomeOtherClass {
test() {}
// @ts-ignore
__reactstandin__regenerateByEval(key, code) {
// @ts-ignore
this[key] = eval(code);
}
}
const _default = 12;
export default _default;
;
(function () {
var reactHotLoader = require('react-hot-loader').default;
var leaveModule = require('react-hot-loader').leaveModule;
if (!reactHotLoader) {
return;
}
reactHotLoader.register(x, 'x', 'test.js');
reactHotLoader.register(blah, 'blah', 'test.js');
reactHotLoader.register(Foo, 'Foo', 'test.js');
reactHotLoader.register(SomeOtherClass, 'SomeOtherClass', 'test.js');
reactHotLoader.register(_default, 'default', 'test.js');
leaveModule(module);
})();
; It looks like there are a few pieces here:
Hopefully I can get this working reasonably soon. I'll probably have some time this weekend. |
__reactstandin__regenerateByEval was created to "inject" a new code, into the old component. The problem - it updates the code "as it exists", not "as it was created". Working theory - change React, or RHL, to work "without" proxy, on hot update - replace the class as a whole, and copy some props (state, variables) from old instance to a new one. |
@theKashey Looks like there have been a few more requests for react-hot-loader support, and the react-hot-loader Babel plugin hasn't had many changes, so I think I'll just implement that. I was actually surprised at how well react-hot-loader works without the plugin, so arguably it's not so important for Sucrase to support it, but looks like Do you know a specific case where the |
We'll want top-level declaration detection for #228 anyway, and hasShadowedGlobals was buggy because it was treating export declarations as shadowing themselves, so this adds some bookkeeping so we know when a declaration is top-level or not. This doesn't affect correctness, it was just an optimization that wasn't kicking in when it was supposed to. Tracking scope depth throughout the parser is a little scary and adds another field to the state, so there may be a way to improve it in the future, but it certainly should work for now.
#367) We'll want top-level declaration detection for #228 anyway, and hasShadowedGlobals was buggy because it was treating export declarations as shadowing themselves, so this adds some bookkeeping so we know when a declaration is top-level or not. This doesn't affect correctness, it was just an optimization that wasn't kicking in when it was supposed to. Tracking scope depth throughout the parser is a little scary and adds another field to the state, so there may be a way to improve it in the future, but it certainly should work for now.
Webpack loader could do the same job.
TLDR - feel free to do nothing :) |
Fixes #228 Some details: * An eval snippet needed to be added to each class, which I decided to do unconditionally for simplicity. * A previous change already comptued the top-level declared variables, so I could just use those. * There was a bug where parameters in arrow function types were seen as top-level variables, so I changed it so types are never considered variable declarations. * In order to register the default export, we need to extract it to a variable, which required modifying both import transformers to handle that as a special case. * The ReactHotLoaderTransformer doesn't actually participate in normal transform, it just adds the snippets to the start and end. Cases not handled yet that could be handled in the future: * Avoid treating `require` statements as top-level declarations. * Skip react and react-hot-loader files themselves (Sucrase shouldn't be running on them anyway). I tested this end-to-end on a small app to make sure hot reloading works, including for bound methods.
I implemented the transform in #376, some details, but generally wasn't so bad. But sounds like hopefully RHL will soon enough not need a Babel transform and I can remove it! Mostly I wanted to make sure Sucrase could support RHL in the meantime, and in case the alternative RHL implementation takes a while or ends up running into trouble. |
So true |
Fixes #228 Some details: * An eval snippet needed to be added to each class, which I decided to do unconditionally for simplicity. * A previous change already comptued the top-level declared variables, so I could just use those. * There was a bug where parameters in arrow function types were seen as top-level variables, so I changed it so types are never considered variable declarations. * In order to register the default export, we need to extract it to a variable, which required modifying both import transformers to handle that as a special case. * The ReactHotLoaderTransformer doesn't actually participate in normal transform, it just adds the snippets to the start and end. Cases not handled yet that could be handled in the future: * Avoid treating `require` statements as top-level declarations. * Skip react and react-hot-loader files themselves (Sucrase shouldn't be running on them anyway). I tested this end-to-end on a small app to make sure hot reloading works, including for bound methods.
I just published this in Sucrase 3.9.0. I did a fair amount of testing and set up the Sucrase website to use RHL with this config, but people should feel free to file issues if they run into trouble! |
Sucrase looks promising for a faster dev experience. But what about support form HMR with react?
The popular solution is to use react-hot-loader but it's a babel plugin... Any existing solution to support this feature with Sucrase?
The text was updated successfully, but these errors were encountered: