Evheniy Bystrov

@evheniybystrov

React Web Project Building

In this article I’ll show you how to create React web project kernel with a new React features: React Suspense and React Lazy.

Who wants to see all code now I created a github repository.

So, Let’s start.

Our first step will be creating directory:

mkdir react-web-project-builder
cd react-web-project-builder

I hope you already installed node.js. We will use lates LTS version (now it’s 10.13.0). I usually use nvm (Node Version Manager). To see current version just run next command:

nvm ls-remote --lts=Dubnium

Next, we should install yarn and create-react-app:

npm i -g yarn create-react-app

And now we are ready to create our react app:

create-react-app .

We can check our package.json file:

cat package.json

And even start our app:

yarn start

After running this command you can see url in the console:

Usually it’s http://localhost:3000/. Let’s open it:

Now we will create our app kernel.

But before, we should check our app structure:

I removed App.test.js because for now we don’t need it (about testing I’ll create a new part of the article).

Let’s create app and page directories:

mkdir src/app src/page

And move src/App.js and src/App.css to src/page directory:

mv src/App.js src/page/index.js
mv src/App.css src/page/index.css

Don’t forget to update src/page/index.js (links to logo and css files):

Create index.js in src/app directory:

touch src/app/index.js

And update src/index.js file:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import AppKernel from './app';
import Page from './page';

import * as serviceWorker from './serviceWorker';

const App = AppKernel(Page);

ReactDOM.render(<App />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();

We just created AppKernel. And It’s not just a component — it’s HOC (higher-order component).

Higher-order components came to us from functional programming. It’s almost the same like higher-order function: it gets a React component and returns a React component.

About React and functional programming you can read in my article:

So, let’s create our first HOC — AppKernel (src/app/index.js):

import React from 'react';

const AppKernel = Component => () => <Component />;

export default AppKernel;

Here AppKernel is a function. It has only one argument: Component.

And it returns function — React component. The function does not anything else. It even does not have props.

To check that it’s working we can run again our app:

yarn start

If you did all steps right you will not see any errors:

Now we are ready to add functionality to our AppKernel.

StrictMode

Strict mode is a tool for highlighting potential problems in an application. Like Fragment, StrictMode does not render any visible UI. It activates additional checks and warnings for its descendants.

And src/app/index.js file after refactoring:

import React, { StrictMode } from 'react';

const AppKernel = (Component) => {
return () => (
<StrictMode>
<Component />
</StrictMode>
);
};

export default AppKernel;

Error Boundaries

In the past, JavaScript errors inside components used to corrupt React’s internal state and cause it to emit cryptic errors on next renders. These errors were always caused by an earlier error in the application code, but React did not provide a way to handle them gracefully in components, and could not recover from them.

You can read how to create your own ErrorBoundary component, but I found a good implementation in a react-error-boundaries package.

To install it run:

yarn add react-error-boundaries
import React, { StrictMode } from 'react';
import {
ErrorBoundary,
FallbackView,
} from 'react-error-boundaries';


const AppKernel = (Component) => {
return () => (
<StrictMode>
<ErrorBoundary FallbackComponent={FallbackView}>
<Component />
</ErrorBoundary>
</StrictMode>
);
};

export default AppKernel;

And again, we can run the app and see that it works.

To check that our error handler works we can make an error, for example in src/page/index.js:

import React, { Component } from 'react';
import logo from '../logo.svg';
import './index.css';

class App extends Component {
render() { throw new Error('testing');
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
}

export default App;

And our error:

Don’t forget to revert this change.

Redux

How we can skip using redux? I use it in all my react apps.

I even created a nice redux library — Redux Lazy. It helps to create redux action types, action creators, reducers and even connect containers in more declarative way (it makes all this stuff for you).

In next part of the article I’ll show how to make a smart component with own logic from the our simple page component.

To use redux we need to install it (and we can install react-redux — we will use it in future):

yarn add redux react-redux

Let’s create our redux store. We will keep it in src/store/index.js:

import { createStore } from 'redux';

const store = createStore(() => {});

export default store;

For now it does nothing. But in next parts of the article I’ll show how to configure it, adding logger, making code splitting with injecting a new reducers.

And our AppKernel:

import React, { StrictMode } from 'react';
import {
ErrorBoundary,
FallbackView,
} from 'react-error-boundaries';
import { Provider } from 'react-redux';

import store from '../store';


const AppKernel = (Component) => {
return () => (
<StrictMode>
<ErrorBoundary FallbackComponent={FallbackView}>
<Provider store={store}>
<Component />
</Provider>
</ErrorBoundary>
</StrictMode>
);
};

export default AppKernel;

And again, we can run our app and see that it’s working well.

React Router

We can add React router.

yarn add react-router react-router-dom

You can read quick start documentation how to use it.

Let’s add it to our AppKernel:

import React, { StrictMode } from 'react';
import {
ErrorBoundary,
FallbackView,
} from 'react-error-boundaries';
import { Provider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom';

import store from '../store';

const AppKernel = (Component) => {
return () => (
<StrictMode>
<ErrorBoundary FallbackComponent={FallbackView}>
<Provider store={store}>
<Router>
<Component />
</Router>
</Provider>
</ErrorBoundary>
</StrictMode>
);
};

export default AppKernel;

And restart our app. It works!

In next part of the article I’ll show how to create router with history.

Suspense

About React Suspense you can read more in react docs: code splitting.

It works with React Lazy.

Let’s add React Suspense to our AppKernel:

import React, { StrictMode, Suspense } from 'react';
import {
ErrorBoundary,
FallbackView,
} from 'react-error-boundaries';
import { Provider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom';

import store from '../store';

const AppKernel = (Component) => {
return () => (
<StrictMode>
<ErrorBoundary FallbackComponent={FallbackView}>
<Provider store={store}>
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Component />
</Suspense>
</Router>
</Provider>
</ErrorBoundary>
</StrictMode>
);
};

export default AppKernel;

You can create nice Loading component and put it as a fallback property to the Suspense wrapper.

We are done with AppKernel.

We created a great HOC. In next part of the app I’ll show how to split this big HOC into small HOCs and reuse all parts or some useful parts in own React projects.

But before we finish I’m going to show how to use React Lazy and code splitting.

The simple solution will be wrap our page component and load it when we start our app.

Let’s update our index.js:

import React, { lazy } from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import AppKernel from './app';
import * as serviceWorker from './serviceWorker';

const Page = lazy(() => import('./page'));

const App = AppKernel(Page);

ReactDOM.render(<App />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();

When we reload our page we can see the same results. And that is the point.

It works the same. But now we split our app into small chunks. We can see them if we check Network tab in dev tools of the browser (I use Chrome):

We can see 1.chunk.js and 2.chunk.js.

To see loading component we can disable cache and set slow connection:

And last thing. I want to check our package.json:

{
"name": "react-web-project-builder",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^16.6.3",
"react-dom": "^16.6.3",
"react-error-boundaries": "^1.1.4",
"react-redux": "^5.1.1",
"react-router": "^4.3.1",
"react-router-dom": "^4.3.1",

"react-scripts": "2.1.1",
"redux": "^4.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}

We can see our new dependencies.

That’s all for now.

More by Evheniy Bystrov

Topics of interest

More Related Stories