react-hot-loader: [React Hooks] Incompatibility with react-hot-loader

Description

The new react hooks API does not work with react-hot-loader. Using the example in my app with export default hot(module)(App); at the bottom of app.js, the following error occurs:

Expected behavior

Should work.

Actual behavior

Uncaught Error: Hooks can only be called inside the body of a function component.

Reproducible Demo

Dunno of a demo is needed, just ping me if you need one.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 86
  • Comments: 76 (12 by maintainers)

Commits related to this issue

Most upvoted comments

Yes. RHL is 100% not compatible with hooks. There is just a few reasons behind it:

  • SFC are being converted to Class components. There is reason - to be able to forceUpdate on HMR, as long there is no “update” method on SFC. I am looking for other way of forcing the update (like this. So RHL is killing SFC.

  • “hotReplacementRender”. RHL is trying to do React’s job, and render the old and the new app, to merge them. So, obviously, that’s broken now.

I am going to draft a PR, to mitigate both problems. It will work, but not today.

4.6.0 just went out. No longer beta. Hooks should work out of the box without any setup needed

I just set pureSFC and everything works fine, this can be a problem for other pieces @theKashey?

setConfig({ pureSFC: true })

There is a more proper fix, which would work my better and ever after - cold API

You may disable RHL for any custom type.

import { cold } from 'react-hot-loader';
cold(MyComponent);

or just search for “useState/useEffect” inside component source code, and ❄️it.

import { setConfig, cold } from 'react-hot-loader'
setConfig({
  onComponentRegister: (type, name, file) =>
    (String(type).indexOf('useState') > 0 ||  String(type).indexOf('useEffect') > 0) && cold(type),
})

Probably me. I already was used to introduce per-component settings to support React.memo, and the same would work for hooks. I’ll release a new version tomorrow, with memo and hooks 🤞, but, probably, without React.Lazy support.

4.5.2 released. All reported issues solved.

Meanwhile, everything depends on this - https://github.com/reactjs/rfcs/pull/74

Yes I’ve just seen the same. I tried removing my hot(module)(App) equivalent but also required removing babel plugin to get hooks to work.

“react-dom” does not support hooks. They are available in alpha-2, or 16.8

React-Hot-Loader is not quite CRA compatible. While we don’t have any good solution for CRA - just setConfig({ pureSFC: true });. That’s enough for hooks to work (if you are using “next” version of RHL).

Probably pureSFC is a good option. Just

  • don’t wrap SFC with hot - top component should be a component to be able to update nested tree on HMR.
  • don’t define contextTypes on it (not a problem for hooks)
  • it would probably fail to reconcile updates, and will generate some error in “hot-render”, and then continue from the next renderable component.

So - just don’t use “hidden” components (ie not exported as a top level variables) - and that would be ok. Also - there could be a problem with force update.

1 - look like source-map went frontend way due to fetch polyfill, probably. It’s fixed in 0.8.0-beta.0, which I probably shall not use yet. See https://github.com/mozilla/source-map/issues/349

2 - this error means that you have ignoreSFC: true, but not @hot-loader/react-dom - it should display a message when hot-patches are detected.

@theKashey the docs for hooks in the readme still seem old?

v4.5.3(or just next) released. False positive merges got fixed, among a set of other problems. 🤞 release candidate.

If I use { test: /\.jsx?$/, include: /node_modules/, use: ['react-hot-loader/webpack'], }, as sad in readme I get multiply messages of save error: _

ERROR in (webpack)/buildin/global.js Module build failed (from ./node_modules/react-hot-loader/webpack.js): TypeError: aSourceMapConsumer.eachMapping is not a function at Function.fromStringWithSourceMap (/Users/chikovvas/activity/code/own/xapnew/node_modules/react-hot-loader/node_modules/source-map/lib/source-node.js:87:24) at Object.transform$1 (/Users/chikovvas/activity/code/own/xapnew/node_modules/react-hot-loader/dist/webpack.development.js:132:59) @ ./node_modules/html-webpack-plugin/node_modules/lodash/lodash.js 1:0-47 @ ./node_modules/html-webpack-plugin/lib/loader.js!./client/index.html

If I try @hot-loader/react-dom alias I get:

Module not found: Error: Can’t resolve ‘lodash.merge’ in ‘/Users/chikovvas/activity/code/own/xapnew/node_modules/react-hot-loader/dist’

until I’ll didn’t remove “react-hot-loader/babel” from .babelrc

@theKashey I am confused now 😦

What is the solution to hot-loading and hooks for create-react-app ? react-app-rewired ?

I really want to avoid to use a forked react-dom if possible.

Then we got all the cases (except npm) covered. I’ve updated react-🔥-dom, and included production version, as long @Bnaya hack could not be undone for prod builds.

the final configuration could be like:

  1. Add webpack-loader or use special version of react-dom.
  2. Set configuration
// hotConf.js. Should be imported before any other React stuff
import { setConfig } from 'react-hot-loader'
import ReactDOM from 'react-dom';

setConfig({
  // if our patch is present - ignore all SFC
  ignoreSFC: !!ReactDOM.setHotElementComparator,
  // set this flag to support SFC if patch is not landed
  pureSFC: true,
  // remove side effect on classes, to make react-dev-tools experience better(go-to-source)
  pureRender: true,
})

Everything could work even without webpack-loader/react-hot-dom, but it could be strange experience, like React.memo could be not updated. (useMemo will be not updated in any case)

In my tests everything is working. What about you?

v4.5.0(next) solves most of 16.6/16.7 issues and could handle anything if you will got your node_modules processed by our webpack-loader. Please give a try and report back. PS: If someone could convert webpack loader to webpack plugin - that would be just great.