Learn By Building Redux From Scratch

Jameson Brown
4 min readFeb 1, 2024

--

The Standard Flow for Redux

When I first started learning Redux, like many, it was paired with React through react-redux. This led me to think that it was all part of Redux and there was a lot to learn, and try to remember. I reached out to a coworker with a related question and his suggestion at first shocked me.

“Build your own version of Redux. Keep the react-redux parts out of it and just build a basic version of the core package.”

I quickly learned just how simple Redux can, and should, be. Since then, I have had more confidence when adding new pieces of state or troubleshooting bugs. This article will take you through this process of building our own version of a simplified Redux.

Whats Needed

A Store API holds the actual state and methods. Here is a list of methods needed to build our own Redux API.

  • createStore to create a new store.
  • getState to get the state from the store.
  • dispatch to update the state on the store.
  • subscribe to subscribe a listener to store updates.

Create Store and State

The createStore method accepts two parameters (reducer and initialState) to start and outputs a store object.

The getState method outputs the current state.

function createStore(reducer, initalState) {
const store = {}
const state = initalState
const listeners = []

/**
* Retrieve the current state of the store.
*
* @return {Object} The current state of the store.
*/
store.getState = () => state

return store
}

We create the store, state, and listener variables. Then create the getState method which simply returns the current state object.

Notice we do not return the state variable directly. This makes sure the only way to get thestate from store is through the public method getState.

Dispatch

The dispatch method uses the earlier provided reducer to create a new state based on the provided parameter (action). Reducers are functions that calculate and return a new state result. Add the following method after your store.getState method.

/**
* Dispatches an action and updates the state by applying the reducer function.
* Calls each listener function in the listeners array.
*
* @param {Object} action - The action to be dispatched.
*/
store.dispatch = (action) => {
state = reducer(state, action)
listeners.forEach((listener) => listener(state))
}

In addition to updating and setting the new state, we also need to notify the store’s listeners of the new state. We will cover this more in the next section.

Subscribe

The subscribe method allows us to add a listener to the store. This listener will be notified whenever state is updated via dispatch method from previous section.

We will also need an unsubscribe function to remove the listener. A trick that is pretty common is to return the unsubscribe method from the subscribe method. This allows the client to store the unsubscribe method for quickly invoking later.

/**
* Subscribes a listener function to the store.
*
* @param {function} listener - The listener function to be subscribed.
* @return {function} - The unsubscribe function.
*/
store.subscribe = (listener) => {
listeners.push(listener)

return store.unsubscribe(listener)
}

/**
* Unsubscribes a listener function from the store.
* @param {function} listener - The listener function to be unsubscribed.
* */
store.unsubscribe = (listener) =>
listeners.splice(listeners.indexOf(listener), 1)

Now Test It Out

Thats it for building your own simplified Redux. There are features Redux provides that are not included in this tutorial. However, this is a great start and should help you see how simple Redux can be on its own. Here is the finished code. A simple, small, and easy to understand file/API.

function createStore(reducer, initalState) {
const store = {}
let state = initalState
const listeners = []

store.getState = () => state
store.dispatch = (action) => {
state = reducer(state, action)
listeners.forEach((listener) => listener(state))
}
store.subscribe = (listener) => {
listeners.push(listener)

return () => {
const index = listeners.indexOf(listener)
listeners.splice(index, 1)
}
}

return store
}

Open node in your terminal and give it a try with a test reducer and initialState. Pretty slick eh? You have a custom built Redux, (at least a base form of it.)

Possible Enhancements

From here, you can add in many more features to enhance your Redux clone. When working with a new Redux feature, come back and implement it on your clone to get a better practical understanding. Here are some possible enhancement ideas:

  • Error handling on existing API.
  • Debug mode for debugging your API.
  • combineReducers method for more complex state.
  • applyMiddleware method for creating an enhancer for the store.

Thanks for reading. Now go code, make dope shit, and share your creations with others!! Leave a comment if you enjoyed this article. Check out my other articles or my portfolio for my side projects.

--

--

Jameson Brown

Software Engineer based in SLC, UT. Full-time building fintech software. Part-time traveler and explorer. https://jamesonb.com