Overview
The purpose of this blog article is to outline and explain the core elements of a Redux setup. You’ll learn how simple it can be to get started with Redux. If you’ve never heard of Redux, it provides lots of benefits for consistently managing front end state. Redux revolves around a centralized state object where all the front end’s information is stored and is the source of truth for data on the front end.
Redux lets us easily manage a large amount of front end data that modern web development demands. This data could be blog post content fetched from an api, the visibility status of a dropdown menu, or form validation errors. In this tutorial, we’ll stick to the basics and only learn about Redux. However, Redux and ReactJS are commonly paired and in Part II, we’ll see how we can use our Redux store to manage React component state.
Redux Concepts
Store
The redux store is where you store all of your app’s information on the frontend. It is a simple javascript object.
Example State Object
const state = {
posts: [
{
id: 1,
title: "A great post"
},
{
id: 2,
title: "The wonderful world of water bowling"
}
],
showMenu: false,
errors: {
name: '',
email: 'A valid email is required'
}
};
A core principle of redux is that this state object cannot be mutated, or changed, in any way except by dispatching actions, more on that later.
Redux Actions
Actions are basically a way to tell Redux what changes you’d like to make to your state object. You define an action as a simple javascript object with a type key.
Example:
const toggleAction = { type: 'TOGGLE_MENU' };
Later, you’ll use the action’s type to manipulate your state in some way. You can also pass in information to your action that you’ll use to change the state. What if you needed to remove a post from the example state above? You’d need to pass in some extra data to help you determine which to delete:
const removePostAction = { type: 'REMOVE_POST', id: 1 };
Here, we decide to add the id key to the action so that we can delete the post in our store above that has an id that matches the id in our action. It doesn’t need to be id, you could decide to add an index key to the action and delete your posts by their position in posts array. It’s all up to you.
So, now we have our state and actions to change it, but how do we actually make that change to our state?
Reducers
The Redux reducer is how you actually change your state. It is a pure function that takes your previous state object, and the action that you dispatched and returns a completely new state. It is important that in your reducer you are not changing the previous state and instead returning an entirely new copy of your state.
Example:
const initialState = {
posts: [],
showMenu: false,
errors: {},
}
function reducer(state = {...initialState}, action) {
switch (action.type) {
case 'TOGGLE_MENU':
return {
...state,
showMenu: !state.showMenu
};
case 'REMOVE_POST':
return {
...state,
posts: state.posts.filter(post => post.id !== action.id)
};
default:
return state
}
}
First, we define an initialState object that will hold the starting values of our Redux state. Using the initialState as the default value for reducer’s state parameter isn’t mandatory, but it makes the code using our redux state simpler by not requiring a lot of undefined checks.
Reducer State Parameter
The state parameter of the reducer function above is either the previous state, or the initialState object if no previous state exists. If you’ve never seen the spread notation before:
{...initialState}
will simply make a shallow copy of the keys inside the initialState object and return them inside a completely new object. The javascript default parameter for state lets us make sure we have some sane defaults when our redux store is first created.
Reducer Action Parameter
The second parameter is your action that you dispatched. Because every action is required to have a type key, we can decide how we want to manipulate our state by passing the action’s type to a switch statement. Then, based on what the action’s type was we return a new state object with whatever changes our action type should have on the state. That’s what you’ll decide.
This is the standard setup, but the reducer is a function you create and control. You could decide to use if…else if…else to check all your action types if you’d like. You just need to make sure you return the original state if the action’s type isn’t found, so that your state doesn’t become undefined.
Spreads With Redux
In the example above, I’ve added how our two actions would potentially manipulate our example state. You’ll notice we are returning more spreads. Remember, spreads copy an object’s keys into an entirely new object, but if you pass additional keys in afterwards, that key will be merged into the existing object’s key and overwrite its value.
Example
// Using the spread, take the existing state and return a new
// object that has showMenu set to false
return {
...state,
showMenu: false
}
// Example of what the spread outputs in long form
return {
posts: state.posts,
showMenu: state.showMenu, // this gets overridden below
errors: state.errors,
showMenu: false, // overwrites the previous showMenu key
}
But if your store gets bigger, it becomes cumbersome to write out every key and spreads are a great shortcut.
Reducer Gotcha
You might be tempted to not use spreads and return just the changes you need:
case 'TOGGLE_MENU':
return {
showMenu: !state.showMenu,
}
However, remember that the reducer returns a new state object. So, only returning the changes you need will override your entire state with the new object returned.
Dispatching Redux Actions
So, you’ve seen how to create an initialState to start your app with some defaults and a reducer to add, remove, or change items in your state. You’ve learned about actions that get passed to your reducer and inform it how to appropriately return a non-mutated, new state object. We’re missing one key piece of information to tie the loop together: dispatching actions.
Dispatching an action is how you tell your redux store that you want to make changes. However, before you can dispatch an action, you first need a store object to dispatch from:
import { createStore } from 'redux';
import reducer from 'reducer.js';
const store = createStore(reducer)
This is a very basic example of how to do that. We import the createStore function from redux and then import our reducer that we created above. Then we create a redux store object with our reducer. The store will default to the structure of our initialState object. With that done, we can start dispatching actions and changing our state:
store.dispatch({ type: 'TOGGLE_MENU' })
console.log(store.getState()) // showMenu will be true
Redux Core Principles Wrap Up
Congrats, you’ve just created your first very basic redux setup! As you can see, the core redux principle are simple to set up. When we get into combining ReactJS with Redux there will be added complexities, but the core of what you’ve just done will be used again and again to manage your centralized state.
I’ve created a quick codepen that has a functioning example of the code we’ve just walked through. Feel free to check it out and play around! As an exercise before Part II, how would you handle an ADD_POST action inside of the reducer? Give it a try in the codepen, we’ll review it next blog post, good luck!
Part 2 Is Now Available
You can find it on this page.