React-hot-loader: Cannot make RHL work with TypeScript

Created on 7 Jan 2018  ·  11Comments  ·  Source: gaearon/react-hot-loader

Description

Cannot make RHL work with TypeScript.

Expected behavior

I want that RHL simply works correctly (see Reproducible Demo), and maintain components states when updated.

Actual behavior

I'm experiencing a full page reload when modifying the Root.tsx component (for example tweaking with style).
On first code edit It seems to work on the browser console, but it does not updates correctly.
The other times I'll edit the code I get the following message
screen shot 2018-01-07 at 23 20 39
and then a full page reload occurs.

I've tried with various solutions, checked the configuration of the other boilerplates, but I cannot make it works :(

Environment

React Hot Loader version: 3.1.3
node -v: 8.9.1
npm -v: 5.6.0
Operating system: macOS High Sierra
Browser: Chrome 63

Reproducible Demo

I have created this boilerplate:
https://github.com/bwlt/react-typescript-boilerplate
just run yarn dev to start.

bug

All 11 comments

There is a reason displayed - Aborted because ./src/index.tsx is not accepted.
As I understand - you changed index.tsx, not the Root, or nested modules - so it works as expected

In any case - I will ask you to switch to the "@next" version, and it also do has TS example - https://github.com/gaearon/react-hot-loader/tree/next/examples/typescript

The problem is that in that case I didn't changed the index.tsx file insted the Root.jsx component, so the reason displayed seems to not reflect my use case.
BTW I have created a next branch on the reproducible demo which use the react-hot-loader@next version.
I have noticed that it works correctly only if I use awesome-typescript-loader instead of ts-loader. Where I can find more informations on this?

That sounds interesting! Let me check.

transpileOnly: true with ts-loader will also works.

@JounQin setting transpileOnly: true did the job. Thank you.
For making the TypeScript check works again I have used fork-ts-checker-webpack-plugin.
The faster build section is very clear how to set up properly the loader.
I confirm that the bug can be considered solved for the react-hot-loader@next version with additional configuration of the ts-loader

I have updated the demo on both branches (the one for react-hot-loader and the one for react-hot-loader@next) and it worked on both versions.

the reason why is bug appear is that react-hot-loader/babel plugin didn't transpile the code inside the if (module.hot) {/* code */ }.
the plugin should transform the code into this:

if (true) {
  module.hot.accept("./src/App.js", function(__WEBPACK_OUTDATED_DEPENDENCIES__) { /* harmony import */ __WEBPACK_IMPORTED_MODULE_3__App__ = __webpack_require__("./src/App.js"); (function () {
    console.log("unique id!");
    render(__WEBPACK_IMPORTED_MODULE_3__App__["default"]);
  })(__WEBPACK_OUTDATED_DEPENDENCIES__); });
}
;

instead we got this( through typescript loader):

if (true) {
    module.hot.accept("./src/Hello.tsx", function () {
        console.log("unique id!");
        // no transform! still using the cached module from last require!
        render("./src/Hello.tsx"));
    });
}
;

my thoughts is that the ts-loader changed the code, leading it unrecognizable to the babel plugin, thus no further transform.
so, changing your code to manually require the refreshed module should do the trick.

if (module.hot) {
  module.hot.accept("./Hello", () => {
    console.log("unique id!");
    // Hello = require("./Hello").Hello;
    // 坑: tsc转出来的js, react-hot-loader/babel插件不认, 需要手动重新require一遍
    render(require("./Hello").Hello);
  });
}

credit @Riokai

@YueminHu - a bit wrong assumption. That is not a react-hot-loader/babel's duty to transpile the code.

Actually, you are running into webpack 2 "issue" with HRM, which described in even RHL readme. Let me be more clear. The line

 module.hot.accept("./src/App.js", function(__WEBPACK_OUTDATED_DEPENDENCIES__) { /* harmony import */ __WEBPACK_IMPORTED_MODULE_3__App__ = __webpack_require__("./src/App.js"); (function () { 

Is generated by __webpack__. In case you did not transpile the __imports__ into requires (is specify "modules":false in babelrc).
If you did transpile (and ts-loaded did it) - webpack will not transform your "common-js" code.
__And you have to require Hello.tsx by yourself__.

This is how webpack's HRM works.
RHL v4 mitigated this problem via module self-acceptance.

@theKashey I think I get your idea. so the webpack transpiled the imports into requires, (which, in this case, it didnt), what's the good of babel plugin anyway? thanks!

Nor ts-loader, nor babel-loader(or both) transpiled imports into requires, and thus webpack did not add "the magic"(auto-require) you could add only for harmony imports.

RHL's babel plugin in this case just adds some RHL related code, to make it works. But that code is absolutely not related to hot-module-replacement stuff. Like a - not our business.

@gaearon the boilerplate was fantastically helpful. I did some additional experimenting (in an effort to get a ts+js configuration) that also worked nicely with styled-components and most importantly Jest. I couldn't have done it without this as a starting point. Here's the results if my work is of any help to anyone else: https://github.com/basement-lab/scratchpad-typescript/

Was this page helpful?
0 / 5 - 0 ratings