Redux


Uncategorized

Updated Dec 17th, 2021

Redux and Redux toolkit

Overview of the redux pattern: We treat our app data or state as a global immutable object. The only way we can change the current state of the app is by dispatching actions (on say a button click for example) and the payload for that action will go to a reducer function to determine what the next state will look like. The store itself is an observable so everything will happen in a reactive way meaning anything that subscribes to the store will be updated with the latest data.

The benefits of this pattern is we have one way data flow that provides a history predictable of all the changes that occur in the app so we can do time-travel debugging.

I am a fan of Context and “useReducer” but many teams still use redux. Almost 60% of react projects are built with redux.

Notes from Mosh

Watched Mosh Hamedami’s Intro on Youtube here. This is the first hour of his 6 hour course.

Based on Facebook’s Flux, Mobx is an alternative to redux.

Leverage a store

The redux dev tools chrome extension is a thing.

Cache or preserve page state, (sort’s hang around).

Easily debug.

Can use with multiple frontend frameworks

Cons: complexity and verbosity

You don’t need to use redux for every project.

Starter project has simple “webpack” setup

Redux is based on the “functional programming” paradigm

Small functions: more concise, easier to debug, easier to test, more scalable

Functions are first-class citizens, meaning we can store them in a variable, pass them as an argument to other functions

Higher order functions: function that takes a function as an argument, or return a function, or both.

Composing and piping. Use “lodash” to help with havign to read fomr right to left and unnecessary parenthesis:

compose(a, b, c)

but to prevent having to read form right to left use “pipe”

const transform = pipe(trim, toLowerCase, wrapInDiv)

Currying:

Technique that takes a function taking “n” arguments and converts it to having a single argument.

Instead of separate parameters with commas, we use parenthesis

function add(a) {
  return function(b) {
    return a + b
  }
}
add(1)(5)

and with arrow functions:

const add2 = a => b => a + b

A pure function always returns the same value if passed the same parameter. NO random values, no current date/time, no global state (DOM, files, db, etc.)

Benefits of pure functions: self-documenting, easily testable, concurrency, cacheable

Immutability goes hand and hand with pure functions ()strings are immutable but objects and arrays are mutable)

Why immutability? predictability, faster change detection, concurrency

Cons to immutability: performance, memory overhead

Practice immutability with objects

Object.assign({}, person, {name: "Bob", age: 30})

A better way is to use the spread operator

const updated = {...person, name: "Bob"}

But these are shallow copies to you need to be careful.

In order to do deep copy:

const updated = {
  ...person,
  address: {
    ...person.address,
    city: "New York"
  },
  name: "Bob"
}

Very verbose which is why we have libraries to help with this

Adding and removing ad updating from array, (good examples)

JavaScript does not prevent object mutations because it is not a pure functional programming language. To get around this we have to work with libraries that offer real immutable data structures, (Immutable, Immer, and Mori).

Quick tour of immutable.js library shown. The package “immer” is preferred by the author and npmtrends.com

import {produce} from "immer"

What’s beautiful about immer is our code looks familiar to us but our data does not get mutated.

Actually Learning Redux

Store is the single source of truth. Cannot directly modify or mutate the store. So we need to write a function that takes the store as an argument and returns the updated store. Use the spread operator or one of the immutability libraries shown earlier.

This function is often called “reducer” and a lot of people gripe about this. how does it know what property to update? The function takes a second parameter that is called an action.

Can have a reducers for each slice of your store.

We dispatch actions.

Major steps: Design the store, define the actions, create a reducer, set up the store.

install redux

An “action” is just a plain JS object that describes what just happened.

Create the reducer. Can use if and else or a switch statement.


function reducer(state = [], action) {
  if (action.type === "bugAdded") {
    return [
      ...state,
      {
        description: action.payload.description,
        resolved: false
      }
    ]
  }
  
  else if (action.type === "bugRemoved") {
    return state.filter(bug => bug.id !== action.payload.id)
  }
  return state
}

See the tutorial for the switch version

In redux the reducers have to be pure functions

create the store

import {createStore} from "redux" 

We can only “getState” from the store. To set the state we need to dispatch an action.

subscribe and unsubscribe from the store.

subscribing:

unsubcribing:

The “dispatch()” method

Since we refer to the names of our action both when we define them and call them it is common practice to create a reference file in case the name is ever changed on the future we can update in one place.

Create an “ActionTypes.js” file

export const BUG_ADDED = "bugAdded"

Then import with asterisk and alias

import * as actions from "./actionTypes"
// reference like this:
actions.BUG_REMOVED

Action Creators: dispatching an action is not easy, we have to type the entire structure of the object. We can instead create a function that can create this object for us. Great again in case there are future changes we can update one place.

Exercise of adding a new feature of “resolving bugs”