Getting Started with React Redux

According to Redux’s documentation,

Redux is a predictable state container for JavaScript applications.

What does that mean?

To put it in simple words, Redux provides one single place for our JavaScript applications to store their state into, i.e., whenever we need to fetch something we need to look no further than a single place called a “store”.

Now before diving into this further, let us understand the different terms used in redux .

Store

As mentioned earlier, a store is one common place for all of our application’s state.

Middlewares

If you’ve ever used node or express, you might be familiar with the concept of middlewares. Redux uses a special kind of add on called middleware to let us customise the dispatch function.

In this tutorial, we will later understand a middleware called redux-thunk which allows us to write action creators that return a function instead of an action.

Reducers

Reducers are functions that take the current state and an action as arguments, and return a new state result, i.e., reducers are responsible for manipulating the state directly, it is only through a reducer that the user can change the state.

A Redux app really only has one reducer function: the “root reducer” function that you will pass to createStore later on. That one reducer function is responsible for handling all of the reducers and actions that are dispatched, and calculating what the entire new state result should be every time.

wait, wait, wait… actions? dispatch? what are those?

Actions

You can think of an action as an event that describes something that happened in the application. In other words, action tells the reducer what to do and how to manipulate the state.

It consists of a “type” variable that tells reducer to perform a task and an optional variable “payload” which contains the data to which reducer uses to manipulate the state.

It is a JavaScript object that looks something like this -

const exampleAction= {type: 'PERFORM_TASK',payload: 'Learn Redux'}

With redux-thunk middleware we can write our functions like this by using a keyword dispatch. Which is more powerful to include and perform more things inside a function.

export const listProducts = () => async (dispatch) => 
{
dispatch({ type: "PERFORM_TASK_LOAD");
dispatch({ type: "PERFORM_TASK", payload: 'Learn Redux');
}

Alright, now how do we go on to create a store?

  1. Create a file for store (eg. store.js) in the root of our react app.
  2. import createStore, combineReducers, applyMiddleware from redux.
import { createStore, combineReducers, applyMiddleware } from "redux";

3. import redux-thunk;

import thunk from “redux-thunk”;

4. Create our root reducer, which will contain all other reducers.

const reducer = combineReducers({/*reducers go here*/});

5. Initialise initial state and our middleware i.e., redux-thunk.

const initialState = {};  const middleware = [thunk]; //there can be multiple middlewares here

6. Finally create our store and pass reducer,initialstate and middlewares

export const store = createStore(
reducer,
initialState,
applyMiddleware(...middleware) // passing middleware
);

Now to make our app work , we need to initialize our react application by wrapping main component in out index.js file with the <Provider>tag and pass the store as a prop.

import { Provider } from 'react-redux'import store from './redux/store'
const rootElement = document.getElementById('root')ReactDOM.render(<Provider store={store}><App/></Provider>,rootElement)

Now, to check if our react-redux app is working or not, we are going to use an browser extension called Redux DevTools.

After installing it we need to install “redux-devtools-extension” .

npm i redux-devtools-extension

and import it in our store , therefore our store.js will look like this.

import { createStore, combineReducers, applyMiddleware } from "redux"; 
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
const reducer = combineReducers({/*reducers go here*/}); const initialState = {}; const middleware = [thunk]; //there can be multiple middlewares hereconst store = createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware)));
export default store;

Now an icon like this will appear on right of url bar of your browser. Click on it to open redux dev tools.

redux dev tools extension

Alright great! We are done setting up our react-redux app.

Let us now create a simple todo app to understand the flow of a redux application.

Store (Store.js)

Our Store will have only one reducer for this app which is created in next step.

import { createStore, combineReducers, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
import { TodoReducer } from "./Reducers/TodoReducers";

const reducer = combineReducers({Todo: TodoReducer});
const initialState = {};const middleware = [thunk];const store = createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;

Reducers (TodoReducers.js)

There will be two cases, one for adding todos and one for removing. We will call these cases by our actions.

export const TodoReducer = (state = { todos: [] }, action) => {switch (action.type) {case "ADD_TODO_SUCCESS":
return {todos: [action.payload, ...state.todos] };
//adds new todo to existing ones
case "REMOVE_TODO_SUCCESS":
return { todos: action.payload };
//removes the todo
default:
return state;
}
};

Actions (TodoActions.js)

export const AddTodoAction = (todo) => (dispatch) => {dispatch({
type: "ADD_TODO_SUCCESS", // calling the reducer
payload: todo, // this will be passed from our react app
});
};
export const RemoveTodoAction = (todo) => (dispatch, getState) => {
const { Todo:{ todos } } = getState();
// getState gives us access to our app state
dispatch({
type: "REMOVE_TODO_SUCCESS",
payload: todos.filter((t) => todo !== t),
});
}

Now finally in our UI or react app we will call the actions as follows ->

const dispatch = useDispatch();const handleSubmit = (e) => {
e.preventDefault();
dispatch(AddTodoAction(todo));
};
const removeHandler = (t) => {
console.log(t);
dispatch(RemoveTodoAction(t));
};

To access all our todos we use “useSelector ”Hook -

const Todo = useSelector((state) => state.Todo);
const { todos } = Todo;

Now we will simply render out To-Dos on the screen.

Source Code and Live Project -

Thank You all for reading. Peace!

Follow me on Instagram -

Roadside Coder

--

--