Using Redux with React

The plan:

  • Create a race condition to justify the use of Redux and Redux Saga
  • Learn Redux and React-Saga along the way

NOTES:

1.

You can not make API calls and routing transitions inside a reducer

This! This basically means you are bound to use some third party library to handle updating state with data returned by the API calls, hello middleware! You must use something like redux-thunk or redux-saga, there is no two ways about it. Because:

  • i can’t imagine a React app that doesn’t make an API call. React is just the View. The data always comes in from somewhere else.
  • we can’t make API calls in reducers, and reducers update state..

2.

  • State is an {}
  • Action is an {}
  • Reducer is a pure function ()

3.

  • Redux will only give you global state that is still local. It will not persist. You refresh the browser and the state is gone

Redux with React

npm i redux react-redux @types/react-redux

This is all you need to do in order to get started

  1. Create a store
  2. Create a reducer
  3. Wrap your App in a <Provider> component to be able to access the store from anywhere in the app
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import { createStore } from 'redux'
import { Provider } from 'react-redux'

const rootReducer = (state = {}, action: {}) => {
  return state
}

const store = createStore(rootReducer)

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

You need to do the following setup once in order to use Redux inside your react app

Create a global store

We need createStore() from redux in order to cerate a store.

import { createStore } from 'redux`

const store = createStore(reducer, preLoadedState) // optional third arg is middleware (say sagas or thunk)

Create a reducer. We need at least one to get started

(previousState, action) => newState

The reducer is a pure function that takes the previous state and an action, and returns the next state.

const reducer = (state = {}, action: {}) => {
  return state
}

Use the store inside the App

Then we’ll use the <Provider> component and wrap our entire app inside it. This will make the entire app (i.e. wherever we want to use values from the store) be able to access the store. <Provider> will take a store property.

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

Actions

Actions are what tell you what to change in state. The reducer will then change the state based on whatever action was given. You can either write an if statement or a switch. Convention is to use switch cases.

const rootReducer = (state = {}, action: {}) => {
  return state
}

will become

const rootReducer = (state = initialState, action: { type: string }) => {
  switch (action.type) {
    case 'INCREMENT':
      // do something
      return { count: state.count + 1 }
    case 'DECREMENT':
      // do something
      return { count: state.count - 1 }
    default:
      return state
  }
}

Action is just an object. type is by convention, you can call it whatever, and it doesn’t necessarily have to be a string, but that’s also convention.

Use stuff from the store

Now comes the fun part, and the part that Redux has improved on, managing the state using hooks

  • useDispatch() - dispatch an action
  • useSelector() - selects relevant parts of the state object, takes a callback
const count = useSelector((state: any) => state.counter) // this will give you just the `count` property from the state
const dispatch = useDispatch() // this will let you send actions

dispatch({ type: 'DECREMENT' })

Action is an object. So whenever you dispatch actions, you’d have to give dispatch() an action object (with the type that we are expecting)