Create-react-app: Feedback wanted: New Hot Reloading

Created on 15 Jun 2019  ·  43Comments  ·  Source: facebook/create-react-app

I’m working on hot reloading again. Got a very raw alpha version working. It preserves state but only for function components. Works with Hooks.

You can try the alpha version (not production ready, super experimental):

Trying in a New Project (NOT FOR PRODUCTION)

npx create-react-app demo --scripts-version wonky-scripts

Trying in an Existing Project (NOT FOR PRODUCTION)

  1. Replace react-scripts dependency with [email protected]
  2. Keep scripts the same
  3. Delete node_modules/.cache
  4. Run Yarn/npm
  5. Start the project and try editing your React components

What It Looks Like

It’s expected that it will work something like on this gif: https://twitter.com/dan_abramov/status/1139876172903395329

Constraints (Library Authors, Read This!)

If your Hook or Component doesn't work with hot reload, keep in mind that:

  • useMemo and useCallback caches are dropped on every edit (otherwise there wouldn't be a way to edit them 😅). If this breaks your code, consider useRef which gives you a semantics guarantee about not getting re-created. Like here. Usually there's a way to restructure the code to be more resilient.
  • Same goes for useEffect. During hot reload, dependency array will be ignored, and even effects with [] dependencies will re-run. If this breaks your component or Hook, there's likely a way to restructure it to fix this. As a bonus, this will make it easier for you to later add more dependencies to it if needed.
  • However, state and refs get preserved between edits. (As an escape hatch for quickly editing mounting animations and similar, note that users can add // @hot reset to the file they're editing, and this will force state reset. This is not a final syntax but we'll document some way to do it.)

If you need help getting your library working, please post in this thread.

Known issues

  • [ ] Hot reload gets stuck if you save a file without changing it
  • [ ] Editing a file that declares Context doesn't preserve state, currently you need to move Context definition to another module and import it
  • [ ] Errors after hot reload don't always get reported (e.g. try importing a non-existent component and then adding a file without exports)
  • [ ] Lint warnings are confusingly duplicated on save
  • [ ] Some libraries don't work with it (please post which ones in this thread)

Please Share Feedback!

In this thread I’d love to hear first feedback from the brave folks who are willing to try this version on their projects. (Remember: it’s NOT usable in production, it’s just an experiment. This is why it has a funny name.)

Does it work for you? Can you make a gif of how it feels in your app, assuming it’s not a secret?

Thanks!

announcement

Most helpful comment

It works perfectly especially with useEffect ;-)
Many Thanks 👏🏻

wonky-scripts

All 43 comments

Works great, but when changing inside a form from react-hook-form, it dropped the state. It doesn't seem to do this with unmanaged inputs specifically, so it seems like a library collision or something.

Here is a link to the file I was testing with, if you can't recreate it with a basic setup

Here is a webm of it happening

EDIT: After rendering the exact same component with hooks in a separate project, i can't get it to reproduce. I think it has something to do with the components wrapping it. Maybe contextapi? The quest continues...

Thanks. Any chance you can get a minimal reproducing project up so I can look closer at why it loses state?

import React, { useContext } from 'react';

const App: React.FC = () => {
  return (
    <form>
      {/* Change component here */}
      <input name="content" />
    </form>
  );
};

const ExampleContext = React.createContext(5);

const Feed = () => {
  const ctx = useContext(ExampleContext);
  return <App />;
};

const WrappedApp = () => (
  <ExampleContext.Provider value={5}>
    <Feed />
  </ExampleContext.Provider>
);

export default WrappedApp;

It has to do with the contexts! not react-hook-form at all.

Webm of it happening with this example!

Oh yeah — for now you’d have to move context definition into a separate file (and import it from the component). That should work.

(I do have a version without this limitation but it’s a bit more invasive so I left it out.)

[EDIT] I removed .npmrc and it worked after that.

For some reason I am ahving trouble pulling wonky scripts:

@@ -67,7 +67,7 @@
-    "react-scripts": "^3.0.1",
+    "wonky-scripts": "0.0.5",
info There appears to be trouble with your network connection. Retrying...
error Couldn't find package "[email protected]" required by `client`  on the "npm" registry.

revert replacing wonky-scripts with react-scripts and yarn has no issues..

Also tried with a tiny app and npm, can't get it work either.

[EDIT] I removed .npmrc and it worked after that.

It works perfectly especially with useEffect ;-)
Many Thanks 👏🏻

wonky-scripts

Ran into an issue hot.js:167 Uncaught TypeError: findHostNodesForHotUpdate is not a function

@bugzpodder It is supposed to be there if you use the ReactDOM that this package internally aliases. So this sounds strange.

hr-hooks
Shouldn't the bottom Hooked instance maintain its state ?

EDIT: seems like this is the expected behaviour as react sees a different tree in place of Hooked (replaced with div) hence it unmounts the existing component.If thats the case this is a non issue.

seems like this is the expected behaviour as react sees a different tree in place of Hooked (replaced with div) hence it unmounts the existing component.If thats the case this is a non issue.

Yeah, that's the reason. Not sure there's anything we can do for that case.

This is a super cool feature, I’m really excited!

Will it be possible to set it up without CRA in a raw webpack setup?

@niwsa what happens if you explicitly set the key prop on the Hooked elements?

@thetrompf setting a stable key worked,the instance state was preserved...

@gaearon still working for me, like the gif in the tweet mentioned above

@gaearon Adding a side effect (useEffect) on the fly resets the state.Is that expected ?

Adding a side effect (useEffect) on the fly resets the state.Is that expected ?

Yeah. Any change to call order will reset the state. We could in theory supporting adding Hooks at the end without resetting the state but it doesn't seem super important right now.

Additionally, it's expected that effects will _always_ reset on every edit, even with []. This is intentional. If it breaks your effects you might wanna rethink how they're structured. Since it also makes it difficult for you to add new dependencies to them when needed.

Will it be possible to set it up without CRA in a raw webpack setup?

Yes, with some work.

I noticed a bug: it gets "stuck" if you save a file without actually editing it. 😛 Will need to look into this closer to an actual release.

Reading the documentation for hooks the first time and trying out the examples in that .. I may raise some false alarms with regard to hot reloading 😄 ... If you have some pointers, as to what to watch out for it would be great.
UPDATE Just saw the known issues update now :)

I added some info in the original post about constraints. Hope this is helpful for library authors.

Couldn't make it work with CRA wonky-scripts:

yarn run v1.15.2
warning ../../package.json: No license field
$ react-scripts start

There might be a problem with the project dependency tree.
It is likely not a bug in Create React App, but something you need to fix locally.

The wonky-scripts package provided by Create React App requires a dependency:

  "jest": "24.7.1"

Don't try to install it manually: your package manager does it automatically.
However, a different version of jest was detected higher up in the tree:

  /Users/adamklein/projects/reusable/node_modules/jest (version: 24.8.0) 

Manually installing incompatible versions is known to cause hard-to-debug issues.

If you would prefer to ignore this check, add SKIP_PREFLIGHT_CHECK=true to an .env file in your project.
That will permanently disable this message but you might encounter other issues.

To fix the dependency tree, try following the steps below in the exact order:

  1. Delete package-lock.json (not package.json!) and/or yarn.lock in your project folder.
  2. Delete node_modules in your project folder.
  3. Remove "jest" from dependencies and/or devDependencies in the package.json file in your project folder.
  4. Run npm install or yarn, depending on the package manager you use.

In most cases, this should be enough to fix the problem.
If this has not helped, there are a few other things you can try:

  5. If you used npm, install yarn (http://yarnpkg.com/) and repeat the above steps with it instead.
     This may help because npm has known issues with package hoisting which may get resolved in future versions.

  6. Check if /Users/adamklein/projects/reusable/node_modules/jest is outside your project directory.
     For example, you might have accidentally installed something in your home folder.

  7. Try running npm ls jest in your project folder.
     This will tell you which other package (apart from the expected wonky-scripts) installed jest.

If nothing else helps, add SKIP_PREFLIGHT_CHECK=true to an .env file in your project.
That would permanently disable this preflight check in case you want to proceed anyway.

P.S. We know this message is long but please read the steps above :-) We hope you find them helpful!

error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

SKIP_PREFLIGHT_CHECK=true worked though

Couldn't make it work with CRA wonky-scripts:

yarn run v1.15.2
warning ../../package.json: No license field
$ react-scripts start

There might be a problem with the project dependency tree.
It is likely not a bug in Create React App, but something you need to fix locally.

The wonky-scripts package provided by Create React App requires a dependency:

  "jest": "24.7.1"

Don't try to install it manually: your package manager does it automatically.
However, a different version of jest was detected higher up in the tree:

  /Users/adamklein/projects/reusable/node_modules/jest (version: 24.8.0) 

Manually installing incompatible versions is known to cause hard-to-debug issues.

If you would prefer to ignore this check, add SKIP_PREFLIGHT_CHECK=true to an .env file in your project.
That will permanently disable this message but you might encounter other issues.

To fix the dependency tree, try following the steps below in the exact order:

  1. Delete package-lock.json (not package.json!) and/or yarn.lock in your project folder.
  2. Delete node_modules in your project folder.
  3. Remove "jest" from dependencies and/or devDependencies in the package.json file in your project folder.
  4. Run npm install or yarn, depending on the package manager you use.

In most cases, this should be enough to fix the problem.
If this has not helped, there are a few other things you can try:

  5. If you used npm, install yarn (http://yarnpkg.com/) and repeat the above steps with it instead.
     This may help because npm has known issues with package hoisting which may get resolved in future versions.

  6. Check if /Users/adamklein/projects/reusable/node_modules/jest is outside your project directory.
     For example, you might have accidentally installed something in your home folder.

  7. Try running npm ls jest in your project folder.
     This will tell you which other package (apart from the expected wonky-scripts) installed jest.

If nothing else helps, add SKIP_PREFLIGHT_CHECK=true to an .env file in your project.
That would permanently disable this preflight check in case you want to proceed anyway.

P.S. We know this message is long but please read the steps above :-) We hope you find them helpful!

error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

Ok, sorry but I have no reproducable scenario because I switched back after a day of trying it out. The biggest problem for me was that the build used to fail on typescript errors and it no longer did that.

More concrete information than “fail” would be really helpful. Fail how?

(I haven’t really tried it with TypeScript so not sure what kind of issues it might run into.)

yeah sorry I totally agree. With react-scripts 3.0.0 my build fails in the terminal whenever I have a typescript error. I run the terminal in my IDE, so I immediately see it and fix the error (even though the IDE points me to the errors as well). But with the wonky-scripts it no longer fails the build in the terminal. It does show the typescript warning though. So I constantly assumed my IDE was wrong with the typescript errors, where it was actually right and the build just didn't show them anymore.

Does it require the use of create react app or I can just add it to existing package.json and use ?

This is great, very exciting. I know a lot of people will love this.

For clarity, I understand that classes will not be supported by this work, even once completed. Is that correct?

For clarity, I understand that classes will not be supported by this work, even once completed. Is that correct?

Class-based components would be recreated, __preserving__ the underlaying tree, but not the component itself.

example

I tested it on this library, it seems like everything is working as expected.

@gaearon I decided to give it another try because I really want this awesomeness but I found out why it doesn't work properly with Typescript projects. The ForkTsCheckerWebpackPlugin is disabled in the webpack config in wonky-scripts. I can't find the source for the wonky-scripts otherwise I would have pointed to it. It is in webpack.config.js:632.

@hmeerlo I enabled the ForkTsCheckerWebpackPlugin in the wonky scripts package, but no errors were reported, so some additional configs make the ts error reporting not work correctly.

@gaearon The error that is not being reported in ts projects is the following:
image

How I can turn off hot reload?

Ok, here we go:

  • Development mode (yarn start) work like a charm, even better then expected;
  • Production mode (yarn build) does not work at all

    • Building with [email protected] result in error:

      Uncaught ReferenceError: __signature__ is not defined at Object.<anonymous> (index.tsx:15) at Object.<anonymous> (index.tsx:57) at s ((index):33) at Object.<anonymous> (index.tsx:75) at Object.<anonymous> (index.tsx:15) at s ((index):33) at Module.<anonymous> (index.tsx:57) at Module.<anonymous> (index.js:15) at s ((index):33) at Object.<anonymous> (index.tsx:57)

    • Building same project with [email protected] results in working build

__Demo__:

Package.json:

{
  "name": "wonky-scripts-demo",
  "version": "1.0.0",
  "private": true,
  "dependencies": {
    "@types/react": "^16.8.24",
    "@types/react-dom": "^16.8.5",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    // ---
    "react-scripts": "3.0.1",
    "wonky-scripts": "0.0.5",
    // ---
    "typescript": "^3.5.3"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [">0.2%", "not dead", "not op_mini all"],
    "development": ["last 1 chrome version", "last 1 firefox version", "last 1 safari version"]
  },
  "devDependencies": {
    "@types/jest": "^24.0.16"
  }
}

does this work with classes or not?

Resets class state, but keeps the children untouched.

Ignoring the dependency array on a useEffect hook invalidates hot reloading in my application. I have a very simple data fetcher hook with useState and useEffect, which ensures we automatically fetch new data when needed, and show a loading indicator in the meantime.

When hot reloading ignores useEffect, it makes my hook go back to the loading state, meaning the developer does not see their change modified quickly on the screen, but instead sees a loading indicator.

function useData(dataId) {
  const [state, setState] = useState("loading");
  useEffect(() => {
    setState("loading");
    retrieve(dataId).then(setState);
  }, [dataId]);
  return state;
}

I have now worked around that with a useRef, but it took me a while to figure out what is going on, given that useEffect seems to have a semantic guarantee to not re-run in the React docs.

Let me know if I should structure this code differently.

Works like charm.

@denis-sokolov I have separated component with useEffect and the actual rendering component in 2 files so when I change renderer everything works.

It's awesome just takes little bit time to update but if we want make the production build wouldn't it work ?

@gaearon updates, it's been a while, any updates ?

doesn't work with react-app-rewired,
react-app-rewired seems to be checking for CRA version to determine which webpack file to use.

CRA changed webpack name from webpack.config.dev to webpack.config.

@a-eid met this frustrating issue as well today...did you managed to find a fix?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

akctba picture akctba  ·  80Comments

sgriffey picture sgriffey  ·  113Comments

andreypopp picture andreypopp  ·  96Comments

daviseford picture daviseford  ·  108Comments

xiaoxiangmoe picture xiaoxiangmoe  ·  89Comments