A sustainable minimal react-app framework composition | Ready for production

Photo by Polina Tankilevitch from Pexels

Management summary

It’s not that easy to create a sustainable react app template and there are many contradictory opinions. This selection is based on the following criteria sorted by importance to me:

  • Sustainability
  • Community support
  • Separation of concerns
  • Performance
  • Flexibility
  • Typescript for better JavaScript
  • React hooks for improved readability and less props-chaos
  • Redux for state management along with Redux-Toolkit for a reduced boilerplate and Redux-Thunk for async back end actions.
  • Immer for better deep-object-manipulation
  • React-i18next for internationalization
  • Webpack for the bundling and build process

Motivation

Refactoring can be a lot of work and especially if it turns out to be more a rewrite than a refactoring. It all started with a recap of pain points that drove me crazy more than just a couple of times:

Let’s get started with the major downsides of my legacy app:

  • Communication between components using functions as props
  • Extensive passing of props between components
  • Single language
  • Back end calls in every component
  • No centralization
  • No type safety
  • A pain to debug
  1. Centralized code for back end calls so it is flexible enough to deal with different (rest) services or APIs
  2. Separate business logic and presentation
  3. Ship as autonomous component for use in other applications
  4. Being able to deal with different time zones, date- and number-formats and languages
  5. Small footprint

The frameworks I’ve chosen

I took the hard way and tried many of nowadays frameworks and approaches. Many medium writers have done a great job and I really enjoyed following them, even if I ended up with a different opinion.

Typescript

What it is: An open source programming language built on JavaScript that brings type safety and is compiled to JavaScript using the TypeScript compiler or babel.

var multiply = (val1,val2) => {
return val1 * val2;
}
var result = multiply(2,4);
console.log(result);
var invalid = multiply('2','asdf');
console.log(invalid);
const multiply = (val1:number,val2:number) => {
return val1 * val2;
}
let x:number = myFunction(2,4);
console.log(result);
let invalid:number = multiply('2','asdf');
console.log(invalid);

React hooks

What it is: A new (recommended) way of writing react components. Instead of extending React.Component, it looks more like a traditional JavaScript function.

class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
setCount(c) {
this.setState({
count: c
});
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setCount(this.state.count + 1)}>Click me</button>
</div>
);
}
};
ReactDOM.render(<Counter />,document.getElementById('root'));
const Counter = () => {
[count, setCount] = React.useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
ReactDOM.render(<Counter />,document.getElementById('root'));

Redux & Redux-Toolkit & Redux-Thunk

What it is: Redux is a predictable state container for JavaScript apps that centralizes the apps state and logic. Redux-Toolkit is an official “booster” to reduce the redux boilerplate and make things easier. Redux is built for synchronous operations and Redux-Thunk is a redux middleware that allows us to write asynchronous code (e.g. rest request). In other words, something like: “Fetch data from http://myapi.com and then update the react state so that all components using it are re-rendered. And of course, don’t block the user interface while fetching”.

const tags = useSelector((state: RootState) => state.metadata.tags)

Immer

What it is: Immer is a tiny package that allows you to work with immutable state in a more convenient way.

var o = JSON.parse(JSON.stringify(this.state.entry));
o.foo.bar = 'my updated value';
this.setState({ entry: o });
//this is creating an object "entry" in my react state
const [entry, setEntry] = useImmer(props.entry);
const setFooBar = (newVal) => {
setEntry(draft => {
draft.foo.bar = newVal;
});
};

React-I18Next

What it is: react-i18next is a powerful internationalization framework for React/React Native which is based on i18next.

import i18next from 'i18next';
...
return (
<div>{i18next.t('mylocalizedProperty')}</div>
);

Webpack

What it is: A tool/framework that is bundling scripts, images, styles etc. so that your app can be packed and shipped.

const appConfig = {
entry: './src/index.tsx',
externals: {
// Use external version of React
"react": "React",
"react-dom": "ReactDOM"
},
plugins: [
new webpack.IgnorePlugin(/react/,/react-dom/),
new copyPlugin({
patterns: [{
from: './locales/**/*.json',
to: './'
}]
})
],
module: {
rules: [
{
test: /\.(ts|js)x?$/,
use: 'babel-loader',
exclude: /node_modules/,
},
],
},
resolve: {
extensions: [ '.tsx', '.ts', '.js' ],
},
output: {
filename: 'my.bundle.js',
path: path.resolve(__dirname, 'dist/app'),
},
devtool: "source-map"
};

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Martin Horvath

Martin Horvath

I'm a consultant working on international projects in the field of geospatial data and customer experience, with a passion for technology and mountaineering.