Real world app setup with React, Webpack 4 and Redux (Part 2)
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 routerconst 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
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 codeconst 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);
You can check the changes in detail here.