Ejecting from Create React App

The goal of create-react-app and react-scripts is zero-configuration React development. The less time you spend configuring development boilerplate, the more time you spend developing components. You should continue to avoid worrying about configuring your app for as long as you can. But at some point, you'll have to bail on create-react-app and maintain your own configuration.

Providing a zero-configuration environment is only possible because many defaults and many limitations are assumed by create-react-app. This is the trade-off. By providing sane defaults for most of the things that React developers have to do but don't want to, you're making a choice for the developer. This is a good thing—being able to punt on decisions early in the development of your application makes you more productive.

React component hot loading is a good example of a limitation of create-react-app. It isn't part of the configuration offered by create-react-app because you probably don't need it early on in your project. But as things become more complex, being able to troubleshoot your components without disrupting their current state is critical. At this point in the project, create-react-app has served its purpose and it's time to eject.

To eject from create-react-app, run the eject script:

npm run eject

You'll be asked to confirm this action, because there's no going back. At this point, it's worth reinforcing the point that you should not eject from create-react-app until it gets in the way. Remember, once you eject from create-react-app, you now assume the responsibilities of maintaining all of the scripts and all of the configuration that was once hidden from view.

The good news is that part of the ejection process involves setting up scripts and configuration values for your project. Essentially, it's the same thing that react-scripts uses internally, except now these scripts and config files are copied into your project directory for you to maintain. For example, after ejecting, you'll see a scripts directory with the following files:

  • build.js
  • start.js
  • test.js

Now if you take a look at package.json, you'll see that the scripts that you invoke using npm now reference your local scripts instead of referencing the react-scripts package. In turn, these scripts use the files found in the config directory that was created for you when you ran eject. Here are the relevant Webpack configuration files found here:

  • webpack.config.dev.js
  • webpack.config.prod.js
  • webpackDevServer.config.js

Remember, these files are copied over from the react-scripts package. Ejecting simply means that you now control everything that was once hidden. It's still set up the exact same way and will remain so until you change it.

For example, let's suppose that you've decided that you need hot module replacement for React in a way that persists component state. Now that you've ejected from create-react-app, you can configure the necessary parts that enable the react-hot-loader tool. Let's start by installing the dependency:

npm install react-hot-loader --save-dev

Next, let's update the webpack.config.dev.js file so that it uses react-hot-loader. This is something that would have been impossible to configure before we ejected. There are two sections that need to be updated:

  1. First, find the following line in the entry section:
      require.resolve('react-dev-utils/webpackHotDevClient'), 
  1. Replace this with the following two lines:
      require.resolve('webpack-dev-server/client') + '?/', 
      require.resolve('webpack/hot/dev-server'), 
  1. Next, you have to add react-hot-loader to the module section of the Webpack configuration. Find the following object:
      { 
        test: /.(js|jsx|mjs)$/, 
        include: paths.appSrc, 
        loader: require.resolve('babel-loader'), 
        options: { 
          cacheDirectory: true, 
        }, 
      }
  1. Replace it with the following:
      { 
        test: /.(js|jsx|mjs)$/, 
        include: paths.appSrc, 
        use: [ 
          require.resolve('react-hot-loader/webpack'), 
          { 
            loader: require.resolve('babel-loader'), 
            options: { 
              cacheDirectory: true, 
            }, 
          } 
        ] 
      }, 

All you're doing here is changing the loader option to the use option so that you can pass an array of loaders. The babel-loader that you were using stays the same. But now you've added the react-hot-loader/webpack loader as well. Now this tool can detect when it needs to hot replace React components when their source changes.

That's all you have to change with your development Webpack configuration. Next, you'll have to change the way that your root React component is rendered. Here's what index.js used to look like:

import React from 'react'; 
import ReactDOM from 'react-dom'; 
import './index.css'; 
import App from './App'; 
import registerServiceWorker from './registerServiceWorker'; 
 
ReactDOM.render(<App />, document.getElementById('root')); 
registerServiceWorker(); 

To enable hot component replacement, you can change index.js so that it looks like this:

import 'react-hot-loader/patch'; 
import React from 'react'; 
import ReactDOM from 'react-dom'; 
import { AppContainer } from 'react-hot-loader'; 
 
import './index.css'; 
import App from './App'; 
import registerServiceWorker from './registerServiceWorker'; 
 
const render = Component => { 
  ReactDOM.render( 
    <AppContainer> 
      <Component /> 
    </AppContainer>, 
    document.getElementById('root') 
  ) 
};
render(App); 
 
if (module.hot) { 
  module.hot.accept('./App', () => { 
    render(App); 
  }); 
} 
 
registerServiceWorker(); 

Let's break down what you've just added:

  1. The import 'react-hot-loader/patch' statement is necessary to bootstrap the react-hot-loader mechanism.
  2. You've created a render() function that accepts a component to render. The component is wrapped with the AppContainer component from react-hot-loader, which handles some of the bookkeeping associated with hot loading.
  3. The first call to render(App) renders the application.
  4. The call to module.hot.accept() sets up a callback function that renders the App component when a new version of the component arrives.

Now your app is ready to receive hot React component updates. It was always able to receive updates when your source changed, but as discussed earlier in the chapter, these updates will wipe out any state in the component before the component is re-rendered. Now that react-hot-loader is in place, you get to keep any state in your components. Let's try it out.

Once you load up the UI, click on the button a few times to change its state. Then, change the style constant to make the font bold:

const style = { 
  fontWeight: 'bold' 
}; 

Once you save this file, you'll notice that the button component has been updated. More importantly, the state hasn't changed! If you clicked on the button twice, it should look like this now:

This was a simple example that involved only one button. But the setup that you've just created by ejecting from create-react-app, tweaking the development Webpack configuration, and changing the way the App component is rendered can support hot component loading with every component you create going forward.

Adding the react-hot-loader package to your project is just one example of the need to eject from create-react-app so that you can tweak the configuration. I would caution against changing what's absolutely necessary. Make sure that you have a specific goal in mind when you change the configuration that create-react-app gives you. In other words, don't undo all the work that create-react-app has done for you.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.246.193