In this tutorial, we’re gonna introduce main concept of Redux: what it is, how to work with Redux Store, Action, Reducers. Then we will practice to understand all of them in a simple practical Redux example.
Related Post: How to connect React with Redux – react-redux example
Overview
Redux
Redux is a state container that helps JavaScript applications to manage state.
=> Whenever we wanna read the state, look into only one single place – Redux Store.
=> Managing the state could be simplified by dealing with simple objects and pure functions.
Redux Store
Store holds the current state so that we can see it as a single source of truth for our data.
With Redux Store, we can:
– access to state via store.getState()
– update state via store.dispatch(action)
– register listeners via store.subscribe(listener)
– unregister listeners via the function returned by store.subscribe(listener)
:
const unsubscribe = store.subscribe(listener); unsubscribe();
*Note: We only have a single store in a Redux application.
=> When we wanna split data handling logic, we combine Reducers instead of using many Stores.
Redux Reducer
Reducer is a pure function that generates a new state based on an Action it receives. These Actions only describe what happened, but don’t describe how state changes.
const countReducer = (state = initialState, action) => { // return state; }
*Note: Reducer must be a pure function:
=> From given arguments, just calculate the next state and return it.
=> No side effects. No API or non-pure function calls. No mutations.
Redux Action
Action is payload of information that is sent to Store using store.dispatch(action)
.
Action must have a type
property that should typically be defined as string constants. It indicates the type of action being performed:
{ type: 'ADD', number } // or { type: 'SET', count }
Sometimes we may wanna move Actions into a separate module (for example, in case our app is getting larger).
We also have functions that return Redux Actions:
const add = ({ number = 1 } = {}) => ({ type: 'ADD', number }); const set = ({ count }) => ({ type: 'SET', count });
Practice
Example overview
This is a simple Redux Application that has:
– count
as the main state that is stored inside Redux Store.
– 4 types of Action: 'ADD'
, 'MINUS'
, 'SET'
, 'RESET'
.
– Reducer with initial state: count
= 0.
We can add/minus a number to count
, set or reset count
value.
Step by step
Install Redux
Add Redux to dependencies
in package.json:
{ ... "dependencies": { ... "redux": "3.7.2" } }
Then run cmd yarn install
.
Create Redux Actions
We have 4 Action creators, each creator simply returns object which contains type
and payload.
// functions that return Redux Actions // ({ number = defaultValue } = {}) const add = ({ number = 1 } = {}) => ({ type: 'ADD', number }); const minus = ({ number = 1 } = {}) => ({ type: 'MINUS', number }); const set = ({ count }) => ({ type: 'SET', count }); const reset = () => ({ type: 'RESET' });
Create Redux Reducer
Reducer has switch
statement that chooses what to do to return new state based on action’s type
and payload:
const countReducer = (state = { count: 0 }, action) => { switch (action.type) { case 'ADD': return { count: state.count + action.number }; case 'MINUS': return { count: state.count - action.number }; case 'SET': return { count: action.count }; case 'RESET': return { count: 0 }; default: return state; } }
Create Redux Store
Just import the createStore
function from Redux, then pass it to Reducer:
import { createStore } from "redux"; // ... const store = createStore(countReducer);
Subscribe & Unsubscribe
const unsubscribe = store.subscribe(() => { console.log(store.getState()); }); // store.dispatch(action)... unsubscribe(); // store.dispatch(action) << not do anything...
listener (inside subscribe()
function) will be invoked any time we call store.dispatch()
, we can call store.getState()
to read the current state inside the callback.
Calling unsubscribe()
is stopping listening to state updates.
Run and Check result
Full code:
import { createStore } from "redux"; // functions that return Redux Actions const add = ({ number = 1 } = {}) => ({ type: 'ADD', number }); const minus = ({ number = 1 } = {}) => ({ type: 'MINUS', number }); const set = ({ count }) => ({ type: 'SET', count }); const reset = () => ({ type: 'RESET' }); // Redux Reducer const countReducer = (state = { count: 0 }, action) => { switch (action.type) { case 'ADD': return { count: state.count + action.number }; case 'MINUS': return { count: state.count - action.number }; case 'SET': return { count: action.count }; case 'RESET': return { count: 0 }; default: return state; } } // Store const store = createStore(countReducer); const unsubscribe = store.subscribe(() => { console.log(store.getState()); }); store.dispatch(add()); store.dispatch(add({ number: 9 })); store.dispatch(reset()); store.dispatch(minus({ number: 2 })); store.dispatch(minus()); store.dispatch(set({ count: 99 })); console.log('unsubscribe...'); unsubscribe(); store.dispatch(add({ number: 1 }));
Result in Browser Console:
Source code
For running:
- yarn install
- yarn run dev-server
or yarn run build
, then yarn run serve
.