Making a Simple Counter App with Redux and React
Since its introduction in May 2013, the React framework has gained a steady popularity as a single, streamlined framework for developing user interfaces. When predictably managing the state container is required, Redux is one of the most popular libraries. In this tutorial, I will take you step by step to creating a simple counter with React and Redux and introduce you to Redux-specific concepts such as actions, dispatch and reducers. At the end, there is a link to the full GitHub repo.
npm install -g create-react-app // to install create-react-app
create-react-app counter-app-with-redux
Import the store in App.js
Notice that we are importing the createStore
function from Redux. Now, with the above set up, we could pass store
down through App and we would be able to access the Redux store. The Store can be perceived as the object that brings the reducers and the actions together.
To do this, Redux provides a function, createStore()
, that returns an instance of the Redux store. A reducer is passed to the store
. This method is used to createStore()
and used in the App.js where most components are imported.
By including the Provider
the Redux store becomes available to any nested component if these components are wrapped with the connect
function. Therefore, the Counter
component needs to be imported in App.js and placed inside the Provider
.
In ./App.js
import React from 'react';import { createStore } from 'redux'import { Provider } from 'react-redux'import Counter from './components/Counter'import counterReducer from './reducers/counterReducer'const store = createStore(counterReducer)function App() {return (<Provider store={store}><Counter /></Provider>);}export default App;
Create an Action / Payload
In their essence, actions are JavaScript objects. Therefore, actions should be familiar in their structure to anyone who used ES6. Actions are payloads of information. Actions send data from our application to our store.
In Actions/Counter.js
import ActionTypes from '../Actions'export const increaseCount = () => ({type: ActionTypes.INCREASE_COUNT})export const decreaseCount = () => ({type: ActionTypes.DECREASE_COUNT})
Create a Reducer
Reducer is the brain of a Redux app. It takes away the state from the individual component manages the entire state of the app in a single place (note that there could be multiple reducers for complex app). The reducer is a pure function that takes an action(payload). The reason that the reducer must be a pure function is that the reducer is not supposed to change any object outside of the function.
In reducers/counterReducer.js
import ActionTypes from '../Actions'const initialState = {count: 0}export default function counterReducer (state = initialState,action){switch(action.type) {case ActionTypes.INCREASE_COUNT:return {count: state.count + 1}case ActionTypes.DECREASE_COUNT:return {count: state.count - 1}default:return state }}
Counter Component
The Counter component that is linked to the Redux store gets the state.count
from the count reducer via mapStateToProps
. Additionally, the Counter component imports increaseCount and decreaseCount from the ../Actions/Counter
. Then they are imported in the bindActionCreators
in mapDispatchToProps
. The mapDispatchToProps()
function passes through as a second argument. The dispatch
function should be included as the argument to mapDispatchToProps
. Including dispatch
, everything is bundled into a single prop value.
The increaseCount is activated onClick of the + button and the decreaseCount onClick of the –button.
In Components/Counter.js
import React, {Component} from 'react'import {Button} from 'semantic-ui-react'import { connect } from 'react-redux'import { bindActionCreators } from 'redux'import {increaseCount,decreaseCount} from '../Actions/Counter'class Counter extends Component {static mapStateToProps = state => {return {count: state.count}}static mapDispatchToProps = dispatch => {return bindActionCreators({increaseCount,decreaseCount},dispatch)}render() {const { increaseCount, decreaseCount } = this.propsreturn(<div><Button onClick={increaseCount} >+</Button><span>{this.props.count}</span><Button onClick={decreaseCount}>-</Button></div>)}}export default connect(Counter.mapStateToProps,Counter.mapDispatchToProps)(Counter)
In Actions/index.js
The strings for the action types are stored in an object in Actions/index.js as key/value pairs. By importing the action types there can be a consistency across the reducers and the actions that utilize them.
const CounterTypes = {INCREASE_COUNT: 'INCREASE_COUNT',DECREASE_COUNT: 'DECREASE_COUNT'}export default {...CounterTypes,}
Final Result
Full Code on GitHub
References