Real world app setup with React, Webpack 4 and Redux (Part 2)

Digvijay Upadhyay
5 min readSep 2, 2018

You can read the part 1 here in case you haven’t.

Navigation with Router

Install the dependency

npm install --save react-router-dom

I’ll not go into the details of the routers, my assumption here is you already know about then if not refer https://medium.com/@pshrmn/a-simple-react-router-v4-tutorial-7f23ff27adf

I will use HashRouter for this one you can also user BrowserRouter

make following changes to src/index.js

import React from 'react';
import './App.scss'
import ReactDOM from 'react-dom';
import App from './App';
import { HashRouter } from 'react-router-dom' // import hash router
const render = () =>
// render the app inside HashRouter
ReactDOM.render(
<HashRouter>
<App/>
</HashRouter>,
document.getElementById('index'));
render();

Create two new pages (or components posing as pages) src/Pages/Home.js and src/Pages/SomePage.js

// src/Pages/Home.js
import React, { Component } from 'react';
class Home extends Component {
render() {
return (
<h2>
Home Page
</h2>
);
}
}
export default Home;// src/Pages/SomePage.js
import React, { Component } from 'react';
class SomePage extends Component {
render() {
return (
<h2>
SomePage Page
</h2>
);
}
}
export default SomePage;

Now we need to add header and map routes to the new Pages, so we change src/App.js as

import React, { Component } from 'react';
import { Switch, Route } from 'react-router-dom'
import Home from './Pages/Home'
import SomePage from './Pages/SomePage'
import { Link } from 'react-router-dom'
class App extends Component {
render() {
return (
<div>
<header>
<nav>
<ul>
<li><Link to='/'>Home</Link></li>
<li><Link to='/somepage'>Roster</Link></li>
</ul>
</nav>
</header>
<div>
<main>
<Switch>
<Route exact path='/' component={Home}/>
<Route path='/somepage' component={SomePage}/>
</Switch>
</main>
</div>
</div>
);
}
}
export default App;

Now let’s run the app! Check the changes here for more details https://github.com/digvijayu/react_webpack_4_from_scratch/commit/9853cd9bed549392803ac529d1a2d09d360ca04e

Both the pages rendered

Integration with Redux

I will not go into the details of how redux works, instead we’ll just focus on the setup.

First we install redux dependency

npm install --save react-redux
npm install --save redux

We create Reducers/index.js which will handle single action

import { combineReducers } from 'redux';// you can keep this reducer in another file and import it from there
// I have kept it here for simplifying
// I would recommend you to break the reducers and actions as well
const appReducer = (state = {}, action) => {
switch (action.type) {
case 'TEXT_CHANGE':
return {
text: action.text
}
default:
return state
}
}
export default combineReducers({
appReducer
});

Create Actions/index.js with single action to change the text, you can create as many as you want

export const textChange = text => ({
type: 'TEXT_CHANGE',
text
})

Let’s create a store and add a provider in index.js

import React from 'react';
import './App.scss'
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import rootReducer from './Reducers'
import App from './App';
import { HashRouter } from 'react-router-dom'
const store = createStore(rootReducer)const render = () =>
ReactDOM.render(
<Provider store={store}>
<HashRouter>
<App/>
</HashRouter>
</Provider>,
document.getElementById('index'));
render();

Let’s check the implementation on Pages/Home.js

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { textChange } from '../Actions';
class Home extends Component {
render() {
let input
return (
<div>
<h2>
Home Page
</h2>
<div>Write a name and check it in SomePage Link</div>
<input
type="text"
onChange={e =>
{
this.props.updateText(input.value)
}
}
ref={node => {
input = node
}}
/>
<div>
You typed: {this.props.text}
</div>
</div>
);
}
}
const mapStateToText = state => ({
text: state.appReducer.text,
});
const mapDispatch = dispatch => ({
updateText: text => dispatch(textChange(text))
});
export default connect(mapStateToText, mapDispatch)(Home);

you can refer to the changes here

Internationalisation (18n)

We’ll be using react-intl for i18n. First we install the package.

npm i react-intl -S

Add the locale files src/Translations/de.json

{
"Home.title": "Startseite",
"Home.input": "Schreiben Sie einen Namen und überprüfen Sie es in SomePage Link.",
"Home.stateText": "Du hast getippt: {text}"
}

src/Translations/en.json.

{
"Home.title": "Home Page (loaded from en.json)",
"Home.input": "Write a name and check it in SomePage Link.(loaded from en.json)",
"Home.stateText": "You typed: {text} (loaded from en.json)"
}

We need to wrap the whole application in IntlProvider component in index.js. Also register the locales being used

import React from 'react';
import './App.scss'
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import rootReducer from './Reducers'
import App from './App';
import { HashRouter } from 'react-router-dom'
// import the locale dependencies
import { IntlProvider, addLocaleData } from 'react-intl';
import locale_en from 'react-intl/locale-data/en';
import locale_de from 'react-intl/locale-data/de';
// import the translation json
import messages_de from "./translations/de.json";
import messages_en from "./translations/en.json";
const messages = {
'de': messages_de,
'en': messages_en
};
addLocaleData([...locale_en, ...locale_de]);// get the language code
const language = navigator.language.split(/[-_]/)[0]; // language without region code
const store = createStore(rootReducer)const render = () =>
ReactDOM.render(
<Provider store={store}>
<HashRouter>
<IntlProvider locale={language} messages={messages[language]}>
<App/>
</IntlProvider>
</HashRouter>
</Provider>,
document.getElementById('index'));
render();

Inside the application we need to make sure every text is written with FormattedMessage . We’ll make changes just in src/Pages/Home.js

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { textChange } from '../Actions';
import { FormattedMessage } from 'react-intl'
class Home extends Component {
render() {
let input
return (
<div>
<h2>
<FormattedMessage
id="Home.title"
defaultMessage="Home Page"
/>
</h2>
<div>
<FormattedMessage
id="Home.input"
defaultMessage="Write a name and check it in SomePage Link"
/>
</div>
<input
type="text"
onChange={e =>
{
this.props.updateText(input.value)
}
}
ref={node => {
input = node
}}
/>
<div>
<FormattedMessage
id="Home.stateText"
defaultMessage="You typed:"
/>
{this.props.text}
</div>
</div>
);
}
}
const mapStateToText = state => ({
text: state.appReducer.text,
});
const mapDispatch = dispatch => ({
updateText: text => dispatch(textChange(text))
});
export default connect(mapStateToText, mapDispatch)(Home);
The output

You can check the changes in detail here.

--

--