Above is a graph that shows you how redux works. Let’s learn it from a real project
I will use Andrei Neagoie robofriend project as an example. Below is a starter template. We will use redux in this starter template
Create a file called constants.js under the src folder. The constants.js store the variable that we will use later.
1. Turn the redux action type into variable so that if there is any misspelling, we can detect it easily.
export const CHANGE_SEARCH_FIELD = "CHANGE_SEARCH_FIELD";export const REQUEST_ROBOTS_PENDING = "REQUEST_ROBOTS_PENDING";export const REQUEST_ROBOTS_SUCCESS = "REQUEST_ROBOTS_SUCCESS";export const REQUEST_ROBOTS_FAILED = "REQUEST_ROBOTS_FAILED";
2. Create a action.js filer under src folder and import the variables from the constants,js
import {CHANGE_SEARCH_FIELD,REQUEST_ROBOTS_PENDING,REQUEST_ROBOTS_FAILED,REQUEST_ROBOTS_SUCCESS,} from "./constants";
3. Set up the action function. Below is how a action function looks. It returns the action type and action payload. The action-type is normally uppercased
export const setSearchField = (text) => {return {type: CHANGE_SEARCH_FIELD,payload: text,};};
4. This is an action that handles ajax call. Remember it needs to return a function instead of an object
For an Ajax call, we normally have 3 states. Ajax call pending, Ajax call success & Ajax call fail
export const requestRobots = () => (dispatch) => {dispatch({ type: REQUEST_ROBOTS_PENDING });fetch("https://jsonplaceholder.typicode.com/users").then((response) => response.json()).then((data) => dispatch({ type: REQUEST_ROBOTS_SUCCESS, payload: data })).catch((error) =>dispatch({ type: REQUEST_ROBOTS_FAILED, payload: error }));};
5.Create a reducer.js under src folder. Import the constants.js and set up the initial state for the search field
import {CHANGE_SEARCH_FIELD,REQUEST_ROBOTS_FAILED,REQUEST_ROBOTS_SUCCESS,REQUEST_ROBOTS_PENDING,} from "./constants";const initialStateSearch = {searchField: "",};
6. Set up a reducer function to handle the incoming function. We usually use a switch statement to handle the incoming function
When the incoming action match the case, we return a new state
export const searchRobots = (state = initialStateSearch, action = {}) => {switch (action.type) {case CHANGE_SEARCH_FIELD:return { ...state, searchField: action.payload };default:return state;}};
7. Set up the initial state of the ajax call
const initialStateRobots = {isPending: false,robots: [],error: "",};
8. Set up the reducer function to handle the ajax call action
export const requestRobots = (state = initialStateRobots, action = {}) => {switch (action.type) {case REQUEST_ROBOTS_PENDING:return { ...state, isPending: true };case REQUEST_ROBOTS_SUCCESS:return { ...state, robots: action.payload, isPending: false };case REQUEST_ROBOTS_FAILED:return { ...state, error: action.payload, isPending: false };default:return state;}};
9. We go to the index.js and import the Provider from react-redux to provide the state to the react project
10. import the createStore, combineReducers, and applyMiddleware from redux
11. import thunkMiddleware from redux-thunk. This is a redux middleware to handle ajax call
12. import the reducer function from reducers.js
//9. import the Provider from react-redux to provide the state to the react projectimport { Provider } from "react-redux";//10. import the createStore, combineReducers, and applyMiddleware from reduximport { createStore, combineReducers, applyMiddleware } from "redux";//11. import thunkMiddleware from redux-thunk. This is a redux middleware to handle ajax callimport thunkMiddleware from "redux-thunk";//12. import the reducer function from reducers.js
13. Declare the rootReducer. P.S. combinereducer function accept an object only
14. Declare the store and apply the redux middleware
//13. Declare the rootReducer. P.S. combinereducer function accept an object onlyconst rootReducer = combineReducers({ searchRobots, requestRobots });//14. Declare the store and apply the redux middlewareconst store = createStore(rootReducer, applyMiddleware(thunkMiddleware));
15. Wrap the <App /> with a Provider and pass the store as props
ReactDOM.render(//15. Wrap the <App /> with a Provider and pass the store as props<Provider store={store}><App /></Provider>,document.getElementById("root"));registerServiceWorker();
16. We go to the app.js and import the connect function from react-redux to connect react and redux
17. import the action from action.js
import React, { Component } from "react";//16. import the connect function from react-redux to connect react and reduximport { connect } from "react-redux";import CardList from "../components/CardList";import SearchBox from "../components/SearchBox";import Scroll from "../components/Scroll";import "./App.css";//17. import the action from action.jsimport { setSearchField, requestRobots } from "../actions";
18. Use the connect function. It accepts 2 parameters. MapStateToProps is a function that tells redux which states you want to pass as props
mapDispatchToProps is a function that tells redux what action you want to pass as props to your react project
//18.Use the connect function. It accepts 2 parameter. MapStateToProps is a function that tell redux which states you want to pass as props// mapDispatchToProps is a function that tell redux what action you want to pass as props to your react projectexport default connect(mapStateToProps, mapDispatchToProps)(App);
19. Declare the mapStateToProps function. It tells redux what states you want to pass as props
Parameter state comes from index.js provider store state(rootReducers)
//19. Declare the mapStateToProps function. It tells redux what states you want to pass as props// parameter state comes from index.js provider store state(rootReducers)const mapStateToProps = (state) => {return {// searchRobots come from the reducer.jssearchField: state.searchRobots.searchField,isPending: state.requestRobots.isPending,robots: state.requestRobots.robots,error: state.requestRobots.error,};};
21. Use the action as props
22. Use the states as props
componentDidMount() {//21. Use the action as propsthis.props.onRequestRobots();}render() {//22. Use the states as propsconst { searchField, onSearchChange, robots, isPending } = this.props;const filteredRobots = robots.filter((robot) => {return robot.name.toLowerCase().includes(searchField.toLowerCase());});return isPending ? (<h1>Loading</h1>) : (<div className="tc"><h1 className="f1">RoboFriends</h1><SearchBox searchChange={onSearchChange} /><Scroll><CardList robots={filteredRobots} /></Scroll></div>);}}
This is how normally a developer applies redux in their react project without using the redux toolkit.
The end project is in the below: