Vite + React: no HMR, page reloads on every change

I’ve been working on a project with Vite & React, using @jihchi/vite-plugin-rescript and @vitejs/plugin-react. I would’ve expected that if we’re able to leverage the standard Vite React plugin, we could benefit from HMR, but it doesn’t seem to be the case. The tiniest changes, eg. a change to a className, causes a full page reload.

Is there something about vite-plugin-rescript that conflicts with React HMR, or is there something I need to configure correctly?

I did a bit more research on this, and hit upon this caveat about React HMR:

The reason why it doesn’t work is that the additional export is not considered as a component (well, CamelCase functions/classes). If a file contains any exports that is not a component, the transform will not mark the component as side-effects free. This is important because anything else other than a component is generally not guaranteed to be side-effects free, but if there are side-effects, to have HMR work properly someone would need to work on cleaning up the side-effect, which is not possible cause what effect it is firing is unknown.

The way I understand this comment, the React plugin will mark modules with non-component exports as impure, and thus disables HMR when those files change. And to count as a component an export has to be both PascalCased and a function.

Could it be that fundamentally the compiled .bs.js subverts HMR by exporting either modules (a PascalCased object containing a make function), or a make function by itself (not PascalCased)? Do we then need to build in a Rescript-specific version of React HMR into vite-plugin-rescript?

For what it’s worth, have you tried to create interface files (*.resi) for each (*.res) file? Fast Refresh* requires you to only export React components.

*: Fast Refresh is enabled in development in @vitejs/plugin-react

4 Likes

I have now tried making a .resi file for my main component (Game.res). It doesn’t seem to help: when I make an edit, even to a single className, state is reset.

With the .resi file in place, the output .bs.js file ends with:

var make = Game;

export {
  make ,
}
/* Form Not a pure module */

As I already noted, I worry that make isn’t recognised as a component export because it isn’t PascalCased. Though I did experiment with editing the .bs.js file directly to export Game directly. That didn’t seem to help either.

I’m curious what the “not a pure module” comment is all about at the end. I don’t suppose it has any bearing on this issue?

Have you experienced Fast Refresh working in your own projects? Should I expect it to work, if I set things up right?

It’s definitely working with my setup but I remember there were some gotchas like correctly setting up include, that’s why I even created a small repo for a working setup.

2 Likes

Could you try to downgrade @vitejs/plugin-react to v4.0.1 and see if it helps?

I delved into the issue a bit, I believe the Fast Refresh stops working after you upgrade to @vitejs/plugin-react@v4.0.2 or above.

edited: Or, you could try to upgrade to @vitejs/plugin-react to v4.1.0 which includes the fix.

2 Likes

Sorry it’s taken me a while to get back to this, but it seems you’re right that 4.0.2 is the problem version. Downgrading to 4.0.1 but also upgrading to 4.1.0 works! (As long as I also lock down all my component files with resi)

2 Likes

Just to confirm for future people having this problem, it does need to be PascalCased, at least in the latest version as of this date.