Nick Dejesus
About

Making a hooks library framework agnostic with Redux

Someone in the use-shopping-cart discord asked if it was possible to use it without React. I immediately responded with, “No”. I immediately felt sadness after. I felt sad that only React developers could make use of this library. While React is the most used framework to date, it still feels unfair.

It reminded me of when I launched my first Android app, called Tekken Chicken (now known as T7 Chicken). I originally wanted to be an Android developer and this was my first attempt at launching something. What better way to learn a new skill than to make something for a community you’re apart of? I had over 10,000 happy users, however, the Tekken players who had iPhones were pissed! After every update I gave on my Facebook page, there were always iOS users asking me, “iOS when?!”

I hated that my app felt exclusive to one platform, and this is ultimately why I switched to being a web developer. Launching something on a website is technically launching something on an iPhone, Android, Mac or PC, probably your car or toothbrush, who knows what else can access a website these days?

When I was asked about whether or not use-shopping-cart could be used without React, I felt those same feelings again. To a much lesser extent, of course. But I hated that you had to be locked in to React to take advantage of it.

It was a really interesting journey, this blog post is going to go over the steps I had to take at a high level.

Let’s talk about redux.

I love redux. It’s awkward being a redux lover out here. Every time someone says “redux” in a room full of JavaScript developers, many people wince, cringe, brace themselves. Proceeded with war stories about that one time they were on a project with redux. I’ve seen the worst and best of redux. I think if there is one problem with redux is that it is not opinionated at all. Anything very useful but unopinionated has a way of spawning an ecosytem of opinionated ways to use unopinionated tools. Despite there being “best practices”, it didn’t stop from so many redux projects from looking nothing like one another, and that can be very frustrating.

Redux is a framework agnostic tool. It made sense to me that the right thing to do was to remake use-shopping-cart but with redux. Fortunately, there exists a nice tool called redux-tool-kit, which provides all of the best practice features out of the box. Normally, with a redux project you’d need to install a handful of libraries to bring it all together. redux-tool-kit provides all those things and more!

A little context…

Luckily, I didn’t have to start from scratch, most of the code for use-shopping-cart is done! The meat and potatoes of use-shopping-cart all comes down to these “Entry” functions. What they do is manage adding items to the cart and formatting things in a way that makes them easy to represent in a UI. An entry is essentially a product in the cart, and there are several functions that interact with the entry in a CRUD-like way:

  • createEntry
  • updateEntry
  • removeEntry
  • updateQuantity

They also manage meta-state values like total cart count and total price of the cart.

Using Redux Tool Kit

The focal point of any redux implementation is called the store. The store is where all of your apps state is held. The only way you can update your store is through actions and reducers. Reducers look at the type of action that was fired and uses the payload to determine how the state should be changed. I like to think of payload as a param that gets passed to an action.

To properly set up a store, you have to pass it a reducer. If you have multiple reducers you’d have to use combineReducers to make them one, but that’s not something I had to worry about.

If you’ve ever felt overwhelmed by the redux ecosystem, you don’t have to worry about any of that anymore. A lot of that chaos is packed into what RTK calls a slice

RTK gives you a function called createSlice:

A function that accepts an initial state, an object full of reducer functions, and a "slice name", and automatically generates action creators and action types that correspond to the reducers and state.

createSlice actually gives you back one reducer from all the reducers you’ve made with it, which definitely lowers the cognitive load.

This is an example of what it looks like from the RTK docs:

import { createSlice } from '@reduxjs/toolkit'
const initialState = { value: 0 }
const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment(state) {
state.value++
},
decrement(state) {
state.value--
},
incrementByAmount(state, action) {
state.value += action.payload
},
},
})
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer

The way use-shopping-cart creates the slice is a bit different, though. Notice on incrementByAmount has an action and a payload value inside? Imagine it being used like this:

incrementByAmount(2)

If for whatever reason you needed to pass a second param to that function, you would only ever get back the first param. The action would look like this:

{
type: 'counter/incrementByAmount',
payload: 2
}

It would look like this no matter how many params you pass to this action. To fix this in redux, you needed to do something wild with redux-thunk and some other library to add extra params.

RTK offers a nice solution out of the box.

Inside the reducers object of a slice, the example shows that you create the function there. The other option is to make an object instead, so instead of:

incrementByAmount: () => {...}

It’ll be:

incrementByAmount: {
reducer: (state, action) => {...},
prepare: (firstParam, secondParam) => {
return {payload: { firstParam, secondParam } }
}
}

This would make it so that the action comes back like this:

{
type: 'counter/incrementByAmount',
payload: {firstParam: 2, secondParam: "some other value"}
}

prepare allows you to take in the params provided to the action, and manipulate it to be anything you need it to be. You can even omit the payload object itself, but it’s best not to stray too far from the standards in place sometimes.

This is what allowed use-shopping-cart to maintain the same API it had when it was a hooks library. So those already familiar with it wouldn’t have to change if they wanted to do something like setItemQuantity(id, quantity)

Nick Dejesus

Hi, I’m Nick, a React Native and Jamstack developer from Boston.
I’m the author of this blog, nice to meet you!

    slashes
    Nick Dejesus
    GitHubTwitterEmail Me