Simplified Redux
Tags: javascript, react, redux, functional
Published: 2023-11-03 13:49:00

---
title: Simplified Redux
published: true
description: 
tags: #javascript #react #redux #fp
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/brl4e2au5bltb2rm0g3k.png
# Use a ratio of 100:42 for best results.
# published_at: 2023-10-23 16:43 +0000
---

In the previous article, [Declarative JavaScript](https://dev.to/sultan99/declarative-javascript-10oh), we discussed how conditional flows can be represented as higher-order functions. In this chapter, we will continue to extend this concept to create [Redux](https://redux.js.org/) reducers. Despite its declining popularity, Redux can serve as a good example of how to reduce the boilerplate code around this library. Before proceeding, I highly recommend reading [the first chapter](https://dev.to/sultan99/declarative-javascript-10oh).

## Abstractions

You may have noticed that whenever you point at some object, if there is a dog alongside you, it will most likely just stare at your fingertip rather than the object itself. Dogs struggle to understand the abstract line between the finger and the intended object, because such a concept simply does not exist in their cognition. Fortunately, humans have this ability, and it allows us to simplify complex things and present them in a simple form.

In the diagram above, a blue square symbolizes an application, and a barrel attached to the box represents a database. Such diagrams help us understand complex architectural solutions without diving into the details of their implementation. The same method can be applied in programming by separating the main code from the secondary code by hiding them behind functions.

```jsx
const initialState = {
  attempts: 0,
  isSigned: false,
}

// action payload 👇      👇 current state
const signIn = pincode => state => ({
  attempts: state.attempts + 1,
  isSigned: pincode === '2023',
})

const signOut = () => () => initialState

const authReducer = createReducer(
  initialState,
  on('SIGN_IN', signIn),
  on('SIGN_OUT', signOut),
  on('SIGN_OUT', clearCookies),
)
```

The input of the `createReducer` function is like the table of contents for a book. It allows us to quickly finding the necessary function based on the action type. Both `signIn` and `signOut` functions update the state object, accepting the action payload and the current state as input. The rest of the code, which involves the action type check and the reducer call, is encapsulated within the `createReducer` and `on` functions.

```jsx
const createReducer = (initialState, ...fns) => (state, action) => (
  fns.reduce(
    (nextState, fn) => fn(nextState, action),
    state || initialState,
  )
)

const on = (actionType, reducer) => (state, action) => (
  action.type === actionType
    ? reducer(action.payload)(state)
    : state
)
```

For enhanced utility, we introduce the helper functions `useAction` and `useStore`:

```jsx
import {useState} from 'react'
import {useDispatch, useSelector} from 'react-redux'

const useAction = type => {
  const dispatch = useDispatch()
  return payload => dispatch({type, payload})
}

const useStore = path => (
  useSelector(state => state[path])
)

const SignIn = () => {
  const [pincode, setPincode] = useState('')
  const signIn = useAction('SIGN_IN')
  const attempts = useStore('attempts')
  
  return (
    <form>
      <h1>You have {3 - attempts} attempts!</h1>
      <input
        value={pincode}
        onChange={event => setPin(event.target.value)}
      />
      <button
        disabled={attempts >= 3}
        onClick={() => signIn(pincode)}>
        Sign In
      </button>
    </form>
  )
}
```

## Lenses

In functional programming, lenses are abstractions that make operations easier with nested data structures, such as objects or arrays. In other words, they are immutable setters and getters. They are called lenses because they allow us to focus on a specific part of an object.

![lens](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nsmip3lrdggdj95j5obh.png)

First, let's see how to update a value without using lenses:

```jsx
const initialState = {
  lastUpdate: new Date(),
  user: {
    firstname: 'Peter',
    lastname: 'Griffin',
    phoneNumbers: ['+19738720421'],
    address: {
      street: '31 Spooner',
      zip: '00093',
      city: 'Quahog',
      state: 'Rhode Island',
    }
  }
}

const updateCity = name => state => ({
  ...state,
  user: {
    ...state.user,
    address: {
      ...state.user.address,
      city: name,
    }
  }
})

const userReducer = createReducer(
  initialState,
  on('UPDATE_CITY', updateCity),
  ...
)
```

That’s pretty ugly, right? Now let's use the `set/get` lenses:

```jsx
const updateCity = name => state => (
  set('user.address.city', name, state)
)

const updateHomePhone = value => state => (
  set('user.phoneNumbers.0', value, state)
)

const useStore = path => useSelector(get(path))
// or by composing
const useStore = compose(useSelector, get)

const homePhone = useStore('user.phoneNumbers.0')
```

Below is an implementation of the lenses. I should note that all of these functions are already available in the [Ramda](https://ramdajs.com/) library.

```jsx
const update = (keys, fn, obj) => {
  const [key, ...rest] = keys
  if (keys.length === 1) {
    return Array.isArray(obj)
      ? obj.map((v, i) => i.toString() === key ? fn(v) : v)
      : ({...obj, [key]: fn(obj[key])})
  }

  return Array.isArray(obj)
    ? obj.map((v, i) => i.toString() === key ? update(rest, fn, v) : v)
    : {...obj, [key]: update(rest, fn, obj[key])}
}

const get = value => obj => (
  value
    .split(`.`)
    .reduce((acc, key) => acc?.[key], obj)
)

const set = (path, fn, object) => (
  update(
    path.split('.'),
    typeof fn === 'function' ? fn : () => fn,
    object,
  )
)

const compose = (...fns) => (...args) => (
  fns.reduceRight(
    (x, fn, index) => index === fns.length - 1 ? fn(...x) : fn(x),
    args
  )
)
```

## Curring functions

A curried function can be referred to as a function factory. We can assemble a function from different places, or we can use them in composition with other functions. This is a truly powerful feature. However, when we call a curried function with all parameters at once, it may look clumsy. For instance, the call of the curried version of `set` would look like this:

```jsx
set('user.address.city')(name)(state)
```

Let's introduce a `curry` function. This function converts any given function into a curried one, and it can be invoked in various ways:

```jsx
set('user.address.city', 'Harrisburg', state) // f(x, y, z)
set('user.address.city', 'Harrisburg')(state) // f(x, y)(z)
set('user.address.city')('Harrisburg', state) // f(x)(y, z)
set('user.address.city')('Harrisburg')(state) // f(x)(y)(z)
```

Quite simple implementation and usage:

```jsx
const curry = fn => (...args) => (
  args.length >= fn.length
    ? fn(...args)
    : curry(fn.bind(undefined, ...args))
)

const set = curry((path, fn, object) =>
  update(
    path.split('.'),
    typeof fn === 'function' ? fn : () => fn,
    object,
  )
)

const get = curry((value, obj) =>
  value
    .split(`.`)
    .reduce((acc, key) => acc?.[key], obj)
)
```

I often notice that many developers, for whatever reason, wrap callback functions in anonymous functions just to pass a parameter.

```jsx
fetch('api/users')
  .then(res => res.json())
  .then(data => setUsers(data)) // 👈 no need to wrap
  .catch(error => console.log(error))   // 👈 no need to wrap
```

Instead, we can pass the function as a parameter. We should remember that the `then` function expects a callback function with two parameters. Therefore, direct passing will be safe only if `setUsers` expects only one parameter.

```jsx
fetch('api/users')
  .then(res => res.json())
  .then(setUsers)
  .catch(console.log)
```

This reminds me of simplifying fractions or equations in basic algebra.

{% katex %}
(2x - 1)(x + 2) = 3(x + 2) \newline
(2x - 1)\cancel{(x + 2)} = 3\cancel{(x + 2)} \newline
 2x - 1 = 3
{% endkatex %}

```jsx
.then(data => setUsers(data))
// 👇 it equals 👆
.then(setUsers)
```

Let's simplify the `updateCity` function:

```jsx
const updateCity = name => state => (
  set('user.address.city', name, state)
)
// 👇 it equals 👆
const updateCity = set('user.address.city')
```

Or, we can place it directly in the reducer without declaring a variable.

```jsx
const userReducer = createReducer(
  initialState,
  on('UPDATE_CITY', set('user.address.city')),
  on('UPDATE_HOME_PHONE', set('user.phones.0')),
  ...
)
```

The most important aspect is that we can now compose the `set` function and perform multiple updates at once.

```jsx
const signIn = pincode => state => ({
  attempts: state.attempts + 1
  isSigned: pincode === '2023',
})

// 'state => ({ ...' is 👇 removed
const signIn = pincode => compose(
  set('attempts', attempts => attempts + 1),
  set('isSigned', pincode === '2023'),
)

const updateCity = name => state => ({
  lastUpdate: new Date(),
  user: {
    ...state.user,
    address: {
      ...state.user.address,
      city: name,
    }
  }
})

// 👆 it equals 👇
const updateCity = name => compose(
  set('lastUpdate', new Date()),
  set('user.address.city', name),
)
```

This programming style is called [point free](https://en.wikipedia.org/wiki/Tacit_programming) and is widely used in functional programming.

![fp in action](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pciyfgizu8h7ilz47px1.gif)

This article ended up being a bit longer than I had initially planned, but I hope you’ve gained some new knowledge. In the next article, we will touch on the topic of the `try/catch` construct and as usual, here's a small teaser for the [next post](https://dev.to/sultan99/do-or-do-not-there-is-no-try-49f2):

```jsx
const result = trap(unstableCode)
  .pass(x => x * 2)
  .catch(() => 3)
  .release(x => x * 10)
```


P.S. You can find the [online demo here](https://codesandbox.io/s/simplified-redux-sr53fl) 📺.