DEV Community

loading...
Cover image for The key concepts of Redux you need to know as a React developer

The key concepts of Redux you need to know as a React developer

albert_hadacek profile image Albert Hadacek Updated on ・4 min read

Redux is the most popular state management library for frontend applications. It can be used with vanilla JavaScript, yet it is often coupled with React. Today we are gonna go over some concepts, that will help you understand the whole Redux flow.

Why do we need Redux?

Redux allows our application to have a single store for its state. Imagine a big object that contains all the information that your components need to reach, or at least most of the state. Some basic state can be stored within the components, such as display password functionality on an input.

This is very convenient when we need to build large applications with a lot of data, we keep it "all" in a single location and we don't have to care about sending props several levels deep in our component tree.

Core parts of Redux

Some parts of the terminology might be a bit confusing for a lot of newcomers, yet after using Redux for a bit, you will get used to it.

Store - The object that holds the global state of our application

Actions - Actions are plain objects with a mandatory type property and potentially a payload property with some additional data (it can be called however we want, payload is pretty common.

Action Creators - Functions that return the action object described above. Used mainly for consistency or additional logic, as they always return the same object, so it is easier to call a function than writing an object literal from scratch.

Reducers - A pure functions that determine how the state in the store should change based on the action they receive.

Dispatch - Dispatch is a method on the store object provided by Redux, which accepts an action and triggers the change of the state

Selectors - Functions that allow us to access certain values on the store, we can get the content of the store with the getState method on the store object

Redux data flow

So we know, that the global state is in our Store. We also know, that a reducer is responsible for changing the state and that actions determine the change as they are dispatched and passed to reducers.

Alt Text

The diagram above shows explains the mentioned events in a more visual fashion.

React with Redux

Now, let's look at some code. To use Redux in our React application we need these two libraries.

npm i redux react-redux
Enter fullscreen mode Exit fullscreen mode

Then we can build a simple counter app.

// index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import { useSelector, useDispatch } from 'react-redux'

const App = () => {
  // Hook for using dispatch functionality
  const dispatch = useDispatch()
  // Hook for using selector functionality
  const count = useSelector(state => state.count)

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => dispatch(increment())}>Add</button>
      <button onClick={() => dispatch(decrement())}>Substract</button>
    </div>
  )
}

// Action creator for incrementing
const increment = () => ({
  type: 'INCREMENT',
})

// Action creator for decrementing
const decrement = () => ({
  type: 'DECREMENT',
})

// Initial state
const initialState = {
  count: 0,
}

// Our reducer function
const counterReducer = (state = initialState, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, count: state.count + 1 }
    case 'DECREMENT':
      return { ...state, count: state.count - 1 }
    default:
      return state
  }
}

// Our store
const store = createStore(counterReducer)

ReactDOM.render(
  // Redux wrapper
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

Enter fullscreen mode Exit fullscreen mode

As you can see there is a lot of boilerplate for even that simple application. Yet, this structure makes a more complex state manageable.

It is important to note, that the reducer function is run by redux even before the first dispatch, so we need to have a default state in our reducer, as it determines the initial state.

Multiple Reducers

In more complex applications we will be managing parts of the state separately, with their own reducers and actions, to do that, we can take advantage of the combineReducers function.

//...
import {combineReducers} from "redux"

const rootReducer = combineReducers({
  counter: counterReducer,
  //...
})

Enter fullscreen mode Exit fullscreen mode

An important note here is, that the combineReducers passes only the part of the state that belongs to the reducer, not the global state, therefore it does not have to be an object.

Asynchronous actions with Thunk

Our simple app does not talk to any API, therefore the code is synchronous. If we wanted to create an asynchronous action, we will run into trouble.


const users = async () => {
  const res = await fetch("someApi/users")
  const data = await res.json()

  return {
    type: 'USERS',
    payload: data
  }
}

Enter fullscreen mode Exit fullscreen mode

Actions have to return plain objects, we return one at first glance, yet the code is compiled by Babel, where the async/await is translated into a more complex code, that does not fulfill the plain object condition.

To solve it, we can install a piece of middleware called redux-thunk, which allows us to either return a plain object or function, if we return a function we get the option to manually dispatch the action after our request is completed.


// We need to change our store creation
//...
import {createStore, applyMiddleware) from "redux"
import thunk from "redux-thunk"

const store = createStore(rootReducer, applyMiddleware(thunk))

Enter fullscreen mode Exit fullscreen mode

Then our action creator will look like this.


const users = () => async (dispatch) => {
  const res = await fetch("someApi/users")
  const data = await res.json()

  dispatch({
    type: 'USERS',
    payload: data
  })

Enter fullscreen mode Exit fullscreen mode

Redux alternatives

There are other management libraries we can use e.g. MobX or Recoil (experimental library by Facebook). If we want to completely omit Redux, we can achieve similar functionality with the combination of Context API and the useReducer hook.

Discussion (4)

pic
Editor guide
Collapse
dmitryscaletta profile image
DmitryScaletta

I'm calling the police @markerikson

Collapse
monfernape profile image
Usman Khalil

Haha. Mark is a great maintainer and I've seen his countless tweets guiding towards correct usage and behavior of Redux. I wonder how he manages all this.

Collapse
stradivario profile image
Kristiqn Tachev

One of the worst things that you can do with "action creator" is to make it async.

Collapse
rubenarushanyan profile image