React Redux starter kit
To understand all the moving parts of React Redux, I developed a simple counter example, using react, redux, thung, actions generators, redux store. You can also use this architecture to build any front end app using react and redux, just remove all files and code related to counter use this structure.
I assume you know basic things like react, redux, store, thungs, this article is just about how to integrate all these things. You can find all the code at
https://github.com/asifsha/react-redux-counter
To begin create a new react app by typing npm create-react-app react-redux-counter
make sure you have the latest version of nodejs, react and create-react-app.
Create a new folder components
in src
and then create another folder app
in it.
create index.js file in app folder
import React from 'react';
import { Route } from 'react-router-dom'
import CounterPage from '../counter/index'
import About from '../about';
import Header from '../common/Header';
import {withRouter} from 'react-router';
import { connect } from 'react-redux';
const App = () => (
<div>
<header>
<Header/>
</header>
<main>
<Route exact path="/" component={CounterPage} />
<Route exact path="/about-us" component={About} />
</main>
</div>
)
function mapStatesToProps(state, ownProps) {
return {
items: state.items
};
}
const mapDispatchToProps = dispatch => ({
}
)
export default withRouter(connect(mapStatesToProps, mapDispatchToProps)(App));
https://raw.githubusercontent.com/asifsha/react-redux-counter/master/src/components/app/index.js
this will be the main component of our app.
add new folder common
in src for shared/ common components, add Header.js in it
import React from 'react';
import { Navbar,Nav,NavItem } from 'react-bootstrap';
import { Link } from 'react-router-dom'
const Header = () => (
<Navbar>
<Nav>
<NavItem componentClass={Link} href='/' to='/'>Home</NavItem>
<NavItem componentClass={Link} href='/about-us' to='/about-us'>About</NavItem>
</Nav>
</Navbar>
)
export default Header;
https://raw.githubusercontent.com/asifsha/react-redux-counter/master/src/components/common/Header.js
this will be our header component for the app.
create counter
folder in components
folder and add index.js and counterForm.js, it can be done in one file but its a good practice to separate out presentation and container components
import React from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import * as counterActions from '../../actions/counterActions';
import CounterForm from './counterForm';
export class CounterPage extends React.Component {
render() {
return (
<CounterForm
count={this.props.count}
onIncrement={this.props.actions.increment}
onDecrement={this.props.actions.decrement}
/>
);
}
}
const mapStateToProps = state => (
{
count: state.counter.count
})
const mapDispatchToProps = dispatch => ({
actions: bindActionCreators(counterActions, dispatch)
}
)
export default connect(
mapStateToProps,
mapDispatchToProps
)(CounterPage);
https://raw.githubusercontent.com/asifsha/react-redux-counter/master/src/components/counter/index.js
import React from 'react';
import PropTypes from 'prop-types';
import { ButtonToolbar } from 'react-bootstrap';;
const CounterForm = ({ count, onIncrement, onDecrement, isIncrementing, isDecrementing }) => {
return (
<div>
<h1>Counter</h1>
<h2>Counter Value</h2>
<p>Count: {count}</p>
<ButtonToolbar>
<button
className="btn btn-primary"
bsStyle="primary"
onClick={onIncrement}>Increment </button>
<button
value={'Decrement'}
className="btn btn-success"
onClick={onDecrement}>Decrement</button>
</ButtonToolbar>
</div>
);
};
CounterForm.propTypes = {
count: PropTypes.number.isRequired,
onIncrement: PropTypes.func.isRequired,
onDecrement: PropTypes.func.isRequired
};
export default CounterForm;
create a folder about
in component folder and add index.js file in it
import React from 'react';
export default () => (
<div>
<h1>About Us</h1>
<p>This is the sample code to understand all the moving parts of React & Redux including Action, Action creators,Redux Thunk, Reducers </p>
</div>
)
https://raw.githubusercontent.com/asifsha/react-redux-counter/master/src/components/about/index.js
replace contents for src/index.js
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { ConnectedRouter } from 'react-router-redux'
import store, { history } from './store/store'
import App from './components/app'
import './index.css'
const target = document.querySelector('#root')
render(
<Provider store={store}>
<ConnectedRouter history={history}>
<div>
<App />
</div>
</ConnectedRouter>
</Provider>,
target
)
https://raw.githubusercontent.com/asifsha/react-redux-counter/master/src/index.js
now we are done with adding all the components now add actions.
create a new folder actions
in src and create 2 files in it actionTypes.js, counterActions.js
export const INCREMENT = 'counter/INCREMENT'
export const DECREMENT = 'counter/DECREMENT'
https://raw.githubusercontent.com/asifsha/react-redux-counter/master/src/actions/actionTypes.js
import * as types from './actionTypes';
export const increment = () => {
return dispatch => {
dispatch({
type: types.INCREMENT
})
}
}
export const decrement = () => {
return dispatch => {
dispatch({
type: types.DECREMENT
})
}
}
https://raw.githubusercontent.com/asifsha/react-redux-counter/master/src/actions/counterActions.js
now create a folder store
in src and add store.js in it
import { createStore, applyMiddleware, compose } from 'redux'
import { routerMiddleware } from 'react-router-redux'
import thunk from 'redux-thunk'
import createHistory from 'history/createBrowserHistory'
import rootReducer from '.././reducers'
export const history = createHistory()
const initialState = {}
const enhancers = []
const middleware = [
thunk,
routerMiddleware(history)
]
if (process.env.NODE_ENV === 'development') {
const devToolsExtension = window.devToolsExtension
if (typeof devToolsExtension === 'function') {
enhancers.push(devToolsExtension())
}
}
const composedEnhancers = compose(
applyMiddleware(...middleware),
...enhancers
)
const store = createStore(
rootReducer,
initialState,
composedEnhancers
)
export default store;
https://raw.githubusercontent.com/asifsha/react-redux-counter/master/src/store/store.js
this is the basic file to configure Redux store
create a new folder reducers
in src folder and add three files initialState.js,
export default {
counter: {
count: 0
}
};
https://raw.githubusercontent.com/asifsha/react-redux-counter/master/src/reducers/initialState.js
I added initialstate for managing the initial state in a separate file
counterReducer.js
import * as types from '../actions/actionTypes';
import initialState from './initialState';
export default (state = initialState.counter, action) => {
switch (action.type) {
case types.INCREMENT:
return {
...state,
count: state.count + 1
};
case types.DECREMENT:
return {
...state,
count: state.count - 1
};
default:
return state;
}
}
https://raw.githubusercontent.com/asifsha/react-redux-counter/master/src/reducers/counterReducer.js
I am adding counter related reducers to its own file
index.js
import {combineReducers} from 'redux';
import counter from './counterReducer';
const rootReducer= combineReducers({
counter
});
export default rootReducer;
https://raw.githubusercontent.com/asifsha/react-redux-counter/master/src/reducers/index.js
index.js is for combining all reducers for the app, write now we only have one reducer i.e counterReducer.
You may need to install dependencies like react, redux, react-dom, history, react-bootstrap, react-redux, react-router-dom, react-router-redux, redux-thunk,
or copy the package.json file and run npm install
{
"name": "react-redux-counter",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^16.4.1",
"react-dom": "^16.4.1",
"react-scripts": "1.1.4",
"history": "^4.7.2",
"react-bootstrap": "^0.32.1",
"react-redux": "^5.0.7",
"react-router-dom": "^4.2.2",
"react-router-redux": "^5.0.0-alpha.9",
"redux": "^3.7.2",
"redux-thunk": "^2.2.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
https://raw.githubusercontent.com/asifsha/react-redux-counter/master/package.json
I got help from these in developing this sample app.