react-hot-loader README

The project's README from github.com/gaearon/react-hot-loader. As one markdown file: README.md.

React Hot Loader

[![Build Status][build-badge]][build] [![version][version-badge]][package] [![Code Coverage][coverage-badge]][coverage] [![MIT License][license-badge]][license]

[![PRs Welcome][prs-badge]][prs] [![Chat][chat-badge]][chat] ![Backers on Open Collective][oc-backer-badge] ![Sponsors on Open Collective][oc-sponsor-badge]

[![Watch on GitHub][github-watch-badge]][github-watch] [![Star on GitHub][github-star-badge]][github-star]

Tweak React components in real time ⚛️⚡️

Watch Dan Abramov's talk on Hot Reloading with Time Travel.

Moving towards next step

React-Hot-Loader has been your friendly neighbour, living outside of React. But it has been limiting its powers and causing not the greatest experience. It's time to make a next step.

React-Hot-Loader is expected to be replaced by React Fast Refresh. Please remove React-Hot-Loader if Fast Refresh is currently supported on your environment.

Install

npm install react-hot-loader

Note: You can safely install react-hot-loader as a regular dependency instead of a dev dependency as it automatically ensures it is not executed in production and the footprint is minimal.

Getting started

  1. Add react-hot-loader/babel to your .babelrc:
// .babelrc
{
  "plugins": ["react-hot-loader/babel"]
}
  1. Mark your root component as hot-exported:
// App.js
import { hot } from 'react-hot-loader/root';
const App = () => <div>Hello World!</div>;
export default hot(App);
  1. Make sure react-hot-loader is required before react and react-dom:
  1. If you need hooks support, use @hot-loader/react-dom

Hook support

Hooks would be auto updated on HMR if they should be. There is only one condition for it - a non zero dependencies list.

❄️ useState(initialState); // will never updated (preserve state)
❄️ useEffect(effect); // no need to update, updated on every render
❄️ useEffect(effect, []); // "on mount" hook. "Not changing the past"
🔥 useEffect(effect, [anyDep]); // would be updated

🔥 useEffect(effect, ["hot"]); // the simplest way to make hook reloadable

Plus

To disable hooks reloading - set configuration option:

import { setConfig } from 'react-hot-loader';

setConfig({
  reloadHooks: false,
});

With this option set all useEffects, useCallbacks and useMemo would be updated on Hot Module Replacement.

Hooks reset

Hooks would be reset if their order changes. Adding, removing or moving around would cause a local tree remount.

Babel plugin is required for this operation. Without it changing hook order would throw an error which would be propagated till the nearest class-based component.

@hot-loader/react-dom

@hot-loader/react-dom replaces the "react-dom" package of the same version, but with additional patches to support hot reloading.

There are 2 ways to install it:

yarn add react-dom@npm:@hot-loader/react-dom
yarn add @hot-loader/react-dom
// webpack.config.js
module.exports = {
  // ...
  resolve: {
    alias: {
      'react-dom': '@hot-loader/react-dom',
    },
  },
};

Old API

Note: There is also an old version of hot, used prior to version 4.5.4. Please use the new one, as it is much more resilient to js errors that you may make during development.

Meanwhile, not all the bundlers are compatible with new /root API, for example parcel is not.

React-Hot-Load will throw an error, asking you to use the old API, if such incompatibility would be detected. It is almost the same, but you have to pass module inside hot.

import { hot } from 'react-hot-loader';
const App = () => <div>Hello World!</div>;
export default hot(module)(App);
  1. Run webpack with Hot Module Replacement:
webpack-dev-server --hot

What about production?

The webpack patch, hot, Babel plugin, @hot-loader/react-dom etc. are all safe to use in production; they leave a minimal footprint, so there is no need to complicate your configuration based on the environment. Using the Babel plugin in production is even recommended because it switches to cleanup mode.

Just ensure that the production mode has been properly set, both as an environment variable and in your bundler. E.g. with webpack you would build your code by running something like:

NODE_ENV=production webpack --mode production

NODE_ENV=production is needed for the Babel plugin, while --mode production uses webpack.DefinePlugin to set process.env.NODE_ENV inside the compiled code itself, which is used by hot and @hot-loader/react-dom.

Make sure to watch your bundle size when implementing react-hot-loader to ensure that you did it correctly.

Limitations

Recipes

Migrating from create-react-app

  1. Run npm run eject
  2. Install React Hot Loader (npm install --save-dev react-hot-loader)
  3. In config/webpack.config.dev.js, add 'react-hot-loader/babel' to Babel loader configuration. The loader should now look like:
  {
    test: /\.(js|jsx)$/,
    include: paths.appSrc,
    loader: require.resolve('babel-loader'),
    options: {
      // This is a feature of `babel-loader` for webpack (not Babel itself).
      // It enables caching results in ./node_modules/.cache/babel-loader/
      // directory for faster rebuilds.
      cacheDirectory: true,
      plugins: ['react-hot-loader/babel'],
    },
  }
  1. Mark your App (src/App.js) as hot-exported:
// ./containers/App.js
import React from 'react';
import { hot } from 'react-hot-loader';

const App = () => <div>Hello World!</div>;

export default hot(module)(App);

Migrating from create-react-app without ejecting

Users report, that it is possible to use react-app-rewire-hot-loader to setup React-hot-loader without ejecting.

TypeScript

As of version 4, React Hot Loader requires you to pass your code through Babel to transform it so that it can be hot-reloaded. This can be a pain point for TypeScript users, who usually do not need to integrate Babel as part of their build process.

Fortunately, it's simpler than it may seem! Babel will happily parse TypeScript syntax and can act as an alternative to the TypeScript compiler, so you can safely replace ts-loader or awesome-typescript-loader in your Webpack configuration with babel-loader. Babel won't typecheck your code, but you can use fork-ts-checker-webpack-plugin (and/or invoke tsc --noEmit) as part of your build process instead.

A sample configuration:

{
  // ...you'll probably need to configure the usual Webpack fields like "mode" and "entry", too.
  resolve: { extensions: [".ts", ".tsx", ".js", ".jsx"] },
  module: {
    rules: [
      {
        test: /\.(j|t)sx?$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            cacheDirectory: true,
            babelrc: false,
            presets: [
              [
                "@babel/preset-env",
                { targets: { browsers: "last 2 versions" } } // or whatever your project requires
              ],
              "@babel/preset-typescript",
              "@babel/preset-react"
            ],
            plugins: [
              // plugin-proposal-decorators is only needed if you're using experimental decorators in TypeScript
              ["@babel/plugin-proposal-decorators", { legacy: true }],
              ["@babel/plugin-proposal-class-properties", { loose: true }],
              "react-hot-loader/babel"
            ]
          }
        }
      }
    ]
  },
  plugins: [
    new ForkTsCheckerWebpackPlugin()
  ]
};

For a full example configuration of TypeScript with React Hot Loader and newest beta version of Babel, check here.

As an alternative to this approach, it's possible to chain Webpack loaders so that your code passes through Babel and then TypeScript (or TypeScript and then Babel), but this approach is not recommended as it is more complex and may be significantly less performant. Read more discussion here.

Parcel

Parcel supports Hot Module Reloading out of the box, just follow step 1 and 2 of Getting Started.

We also have a full example running Parcel + React Hot Loader.

Electron

You need something to mark your modules as hot in order to use React Hot Loader.

One way of doing this with Electron is to simply use webpack like any web-based project might do and the general guide above describes. See also this example Electron app.

A webpack-less way of doing it to use electron-compile (which is also used by electron-forge) - see this example. While it requires less configuration, something to keep in mind is that electron-compile's HMR will always reload all modules, regardless of what was actually edited.

Source Maps

If you use devtool: 'source-map' (or its equivalent), source maps will be emitted to hide hot reloading code.

Source maps slow down your project. Use devtool: 'eval' for best build performance.

Hot reloading code is just one line in the beginning and one line at the end of each module so you might not need source maps at all.

Linking

If you are using npm link or yarn link for development purposes, there is a chance you will get error Module not found: Error: Cannot resolve module 'react-hot-loader' or the linked package is not hot reloaded.

There are 2 ways to fix Module not found:

{
  resolve: {
    alias: {
      'react-hot-loader': path.resolve(path.join(__dirname, './node_modules/react-hot-loader')),
    }
  }
}

And to make your linked package to be hot reloaded, it will need to use the patched version of react and react-dom, if you're using webpack, add this options to the alias config

{
  resolve: {
    alias: {
      'react-hot-loader': path.resolve(path.join(__dirname, './node_modules/react-hot-loader')),
      // add these 2 lines below so linked package will reference the patched version of `react` and `react-dom`
      'react': path.resolve(path.join(__dirname, './node_modules/react')),
      'react-dom': path.resolve(path.join(__dirname, './node_modules/react-dom')),
      // or point react-dom above to './node_modules/@hot-loader/react-dom' if you are using it
    }
  }
}

Preact

React-hot-loader should work out of the box with preact-compat, but, in case of pure preact, you will need to configure it:

import reactHotLoader from 'react-hot-loader';
import preact from 'preact';

reactHotLoader.preact(preact);

Preact limitations

React Native

React Native supports hot reloading natively as of version 0.22.

Using React Hot Loader with React Native can cause unexpected issues (see #824) and is not recommended.

Webpack plugin

We recommend using the babel plugin, but there are some situations where you are unable to. If so, try the webpack plugin / webpack-loader (as seen in v3).

Remember - the webpack plugin is not compatible with class-based components. The babel plugin will inject special methods to every class, to make class members (like onClick) hot-updatable, while the webpack plugin would leave classes as is, without any instrumentation.

class MyComponent extends React.Component {
  onClick = () => this.setState(); // COULD NOT UPDATE
  variable = 1; // this is ok
  render() {} // this is ok
}

But webpack-loader could help with TypeScript or spreading "cold API" to all node_modules.

It is possible to enable this loader for all the files, but if you use babel plugin, you need to enable this loader for react-dom only. Place it after babel-loader, if babel-loader is present.

// webpack.config.js
module.exports = {
  module: {
    rules: [
      // would only land a "hot-patch" to react-dom
      {
        test: /\.js$/,
        include: /node_modules\/react-dom/,
        use: ['react-hot-loader/webpack'],
      },
    ],
  },
};

Webpack plugin will also land a "hot" patch to react-dom, making React-Hot-Loader more compliant to the principles.

If you are not using babel plugin you might need to apply webpack-loader to all the files.

{
  test: /\.jsx?$/,
  include: /node_modules/,
  use: ['react-hot-loader/webpack']
},

Code Splitting

If you want to use Code Splitting + React Hot Loader, the simplest solution is to pick a library compatible with this one:

If you use a not-yet-friendly library, like react-async-component, or are having problems with hot reloading failing to reload code-split components, you can manually mark the components below the code-split boundaries.

// AsyncHello.js
import { asyncComponent } from 'react-async-component';

// asyncComponent could not `hot-reload` itself.
const AsyncHello = asyncComponent({
  resolve: () => import('./Hello'),
});

export default AsyncHello;

Note that Hello is the component at the root of this particular code-split chunk.

// Hello.js
import { hot } from 'react-hot-loader/root';

const Hello = () => 'Hello';

export default hot(Hello); // <-- module will reload itself

Wrapping this root component with hot() will ensure that it is hot reloaded correctly.

Out-of-bound warning

You may see the following warning when code-split components are updated:

React-Hot-Loader: some components were updated out-of-bound. Updating your app to reconcile the changes.

This is because the hot reloading of code-split components happens asynchronously. If you had an App.js that implemented the AsyncHello component above and you modified AsyncHello, it would be bundled and reloaded at the same time as App.js. However, the core hot reloading logic is synchronous, meaning that it's possible for the hot reload to run before the updates to the split component have landed.

In this case, RHL uses a special tail update detection logic, where it notes that an an update to a split component has happened after the core hot reloading logic has already finished, and it triggers another update cycle to ensure that all changes are applied.

The warning is informational - it is a notice that this tail update logic is triggered, and does not indicate a problem in the configuration or useage of react-hot-loader.

If the tail update detection is not something you want or need, you can disable this behavior by setting setConfig({ trackTailUpdates:false }).

Checking Element types

Because React Hot Loader creates proxied versions of your components, comparing reference types of elements won't work:

const element = <Component />;
console.log(element.type === Component); // false

React Hot Loader exposes a function areComponentsEqual to make it possible:

import { areComponentsEqual } from 'react-hot-loader';
const element = <Component />;
areComponentsEqual(element.type, Component); // true

Another way - compare "rendered" element type

const element = <Component />;
console.log(element.type === <Component />.type); // true

// better - precache rendered type
const element = <Component />;
const ComponentType = <Component />.type;
console.log(element.type === ComponentType); // true

But you might have to provide all required props. See original issue. This is most reliable way to compare components, but it will not work with required props.

Another way - compare Component name.

Not all components have a name. In production displayName could not exists.

const element = <Component />;
console.log(element.displayName === 'Component'); // true

This is something we

react-hot-loader (gaearon/react-hot-loader)

Searching Database…

Что это такое?React Hot Loader позволяет на лету править React-компоненты во время разработки: изменения сразу появляются в работающем приложении без полной перезагрузки страницы и (в большинстве случаев) без потери состояния компонентов. Сейчас он объявлен устаревшим в пользу React Fast Refresh.
Это библиотека или плагин для webpack?Это небольшая JavaScript-библиотека плюс плагин для Babel (и необязательный плагин или загрузчик для webpack), которые добавляются в существующую сборку React-проекта, а не самостоятельное приложение.
Как добавить его в проект?Установите react-hot-loader, добавьте 'react-hot-loader/babel' в свой .babelrc, отметьте корневой компонент как hot-exported и убедитесь, что react-hot-loader подключается раньше react и react-dom.
Нужно ли что-то кроме самого react-hot-loader?Нужно, чтобы в webpack была запущена Hot Module Replacement (горячая замена модулей), а для поддержки хуков (Hooks) вместо react-dom нужно использовать пакет @hot-loader/react-dom.
Стоит ли всё равно использовать его вместо Fast Refresh?Нет: проект прямо советует удалить его, если ваше окружение (CRA 4+, Next.js 9.4+, webpack с плагином Fast Refresh, React Native 0.61+) уже поддерживает Fast Refresh, так как от него постепенно отказываются.
Кому он всё ещё нужен?В основном тем, кто работает на старых сборщиках или конфигурациях (например, Parcel 1, самописные конфигурации webpack без плагина Fast Refresh или проекты на Electron), где Fast Refresh пока недоступен.
Сохраняет ли он состояние компонентов при правках?Да, это его главная цель: он обновляет текущий код, не повторяя события жизненного цикла, однако изменения в конструкторах или уже созданном состоянии компонентов не всегда могут примениться.
Работает ли он с React Hooks?Да: хуки автоматически обновляются при HMR, если у них непустой список зависимостей, а при изменении порядка хуков происходит локальное перемонтирование; для этого нужны плагин Babel и пакет @hot-loader/react-dom.
Безопасно ли оставлять его в продакшен-сборке?Да, в README сказано, что плагин Babel, патч для webpack и @hot-loader/react-dom безопасны для продакшена, так как при NODE_ENV=production они переключаются в режим очистки с минимальным следом.
Работает ли он с TypeScript или Preact?Да, с обоими: пользователи TypeScript могут прогонять код через Babel вместо tsc для горячей перезагрузки, а Preact работает с дополнительной ручной настройкой (однако HOC и декораторы там не поддерживаются).
Creator
gaearon
Size
9.2 MB
Stars
12.1K
Age
12y
Commits · 90 Days