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

Digvijay Upadhyay
Sep 2, 2018 · 5 min read

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.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade