Parcel: React Refresh falls back to full page reload

Created on 16 Feb 2020  路  10Comments  路  Source: parcel-bundler/parcel

馃悰 bug report

Given an additional export in a module Comp.tsx with a React component, whose code I want to change, React Refresh does not apply the changes on the fly, but instead triggers a full page reload.

I did not have found any documentation, what rules we have to follow, in order to let React Refresh do its job. From the examples I have read (like here), it should work just out of the box without restrictions (?).

In the sample, I used TypeScript, but renaming files to .jsx doesn't alter behavior.

馃帥 Configuration (.babelrc, package.json, cli command)

CLI:

parcel index.html

No configs used except tsconfig.json. It shouldn't matter, but config can be found here.

馃 Expected Behavior

Hot refresh without page reloading.

馃槸 Current Behavior

Full page reload.

馃拋 Possible Solution

Remove the second export? 馃槀 Seriously: am I missing something?

馃敠 Context

There should be clear rules, when React Refresh works and when not. Above behavior was unexpected and confusing for me (I still don't understand the cause).

馃捇 Code Sample

Full repo (follow readme) :https://github.com/ford04/parcel-v2-react-refresh

Comp.tsx:

import React, { useState } from "react";

export const Comp = () => {
  const [s, setS] = useState(0);
  return (
    <div>
      <button onClick={() => setS(s + 1)}>Click me</button>
      <div>{s}</div>
      <p>Change my text</p>
    </div>
  );
};

// if this is uncommented, react refresh will fail and reload whole page.
export const PLACEHOLDER = { a: "foo", b: 42 };

馃實 Your Environment

| Software | Version(s) |
| ---------------- | ---------- |
| Parcel | 2.0.0-nightly.110
| Node | v13.5.0
| npm/Yarn | 6.13.4
| Operating System | Win 10

Question HMR

Most helpful comment

I can reproduce this: what happens here is that if I remove the line then the react refresh fails. If the line is kept the refresh succeeds.

Fast Refresh as implemented is quite brittle, it depends on heuristics that all exports from a module are _likely_ to be components, see:
https://github.com/parcel-bundler/parcel/blob/v2/packages/transformers/react-refresh-wrap/src/helpers/helpers.js#L80
The heuristics are implemented here: https://github.com/facebook/react/blob/master/packages/react-refresh/src/ReactFreshRuntime.js#L650

The behaviour I observe is consistent with the code: if you export an object it is not deemed likely to be a component. This then marks the entire module to not be a react refresh boundary.

The behaviour of fast refresh is surprising, especially so because of its lack of documentation. This lack can certainly be considered a bug.

All 10 comments

@mischnic Weren't you able to reproduce the problem from the given repo? From my point of view, this rather looks like a bug than a question.

I haven't tested it yet. But this might be a limitation of React Fast Refresh, I'll take a look at this when I have time.

@ford04 I had the same problem. try moving your App component out of index.tsx and into its own file (app.tsx) with a default export. You should then only have your App import and the render inside index.tsx. This should fix React Fast Refresh.

@crazyricardo thanks, that is also kinda my observation.

Meanwhile, I have found a possible explanation in React native docs:

If you edit a module that only exports React component(s), Fast Refresh will update the code only for that module, and re-render your component.
If you edit a module with exports that aren't React components, Fast Refresh will re-run both that module, and the other modules importing it.
Finally, if you edit a file that's imported by modules outside of the React tree, Fast Refresh will fall back to doing a full reload.

Also:

Here's a few reasons why you might see local state being reset on every edit to a file:

  • Local state is not preserved for class components (only function components and Hooks preserve state).
  • The module you're editing might have other exports in addition to a React component.
  • Sometimes, a module would export the result of calling higher-order component like createNavigationContainer(MyScreen). If the returned component is a class, state will be reset.

If I interpret the statements correctly, PLACEHOLDER export inside Comp.tsx is a non-React component, so index.tsx (React root module) importing Comp.tsx gets also reloaded. It's still a bit unclear to me though, why a full page reload is applied as opposed to just all React components getting refreshed - my index.tsx is an importing module inside the React tree. I haven't tested further, if the React root module with ReactDOM.render(...) is a special case itself.

Maybe link the site somewhere in Parcelv2 docs? This is very useful information for React refresh usage and limitations. We could even notify about a canceled React refresh in Parcel CLI and console itself to improve developer experience,

I can reproduce this: what happens here is that if I remove the line then the react refresh fails. If the line is kept the refresh succeeds.

Fast Refresh as implemented is quite brittle, it depends on heuristics that all exports from a module are _likely_ to be components, see:
https://github.com/parcel-bundler/parcel/blob/v2/packages/transformers/react-refresh-wrap/src/helpers/helpers.js#L80
The heuristics are implemented here: https://github.com/facebook/react/blob/master/packages/react-refresh/src/ReactFreshRuntime.js#L650

The behaviour I observe is consistent with the code: if you export an object it is not deemed likely to be a component. This then marks the entire module to not be a react refresh boundary.

The behaviour of fast refresh is surprising, especially so because of its lack of documentation. This lack can certainly be considered a bug.

Fast Refresh as implemented is quite brittle, it depends on heuristics that all exports from a module are likely to be components

This is "per design", and now also documented here: https://parcel2-docs.now.sh/recipes/react/#exporting-values-that-are-not-components-will-reset-the-state%3A (and also in the official docs: https://reactnative.dev/docs/fast-refresh#limitations)

Please tell me if these docs could be improved or some behaviour you are seeing isn't described.

Great! That documentation covers it!

I did not know there was a documentation for parcel 2 due to _Note: This page covers Parcel 1, the documentation for Parcel 2 has yet to be written._ on https://parceljs.org/getting_started.html.

I did not know there was a documentation for parcel 2

I want to have most pages (except for the plugin authoring part) ready and reviewed by some team members before putting it on the homepage. I hope this will happen very soon

Where is the repo/branch for the new documentation?

Was this page helpful?
0 / 5 - 0 ratings