loading...
Cover image for Redux Basics Explained From A Beginner's Perspective

Redux Basics Explained From A Beginner's Perspective

dylanmesty profile image Dylan Mestyanek Updated on ・10 min read

Throughout last week, I had my first taste of Redux. During this time, we implemented Redux with React but, it does not need to be used exclusively with React. However, this has been my only experience with it thus far, so I will explain it the way it is used with React.

Upon introduction to Redux, you may be left feeling instantly confused. Initially learning React, most days are spent getting comfortable with the idea of passing props from one component, to another, to another... to another.... to.... another.

While this is an easy concept to understand, it's not necessarily the most efficient. There are a variety of state management systems used within React, but I want to discuss Redux and what has helped me wrap my mind around it!

What is Redux?

Redux has one main advantage, and that's the efficiency it provides. Redux allows you to store your state in what is called a "Redux Store" and uses actions to call reducers, which in turn manipulate your state however you see fit.

Let's keep this simple and straight to the point. Redux is Uber Eats.

I know what you may be thinking... What are you are talking about? Let me explain.

In traditional prop passing, relate each component to a neighbor. If you needed something from the grocery store, imagine that you have to ask neighbor E, to ask neighbor D, to ask neighbor C, to ask neighbor B, to ask neighbor A, if you can use some of their bread. It works... but, it's pretty inconvenient.

What if there was a way to just have the bread delivered straight to you?!

AH, this is where Redux shines. With the use of the Redux store, that bread (AKA state), is always available whenever you need it. No passing props, no talking to neighbors, just simply call up the store and get what you need!

The Redux Store

The Redux Store takes about 3.87 seconds to build, and is one of the easiest things to do in React. After installing Redux with your package manager of choice, simply import the function into your main component (usually index.js).

import { createStore } from 'redux'

Boom! Now you have the power, just create a store really quick! Be sure to export your reducer from it's proper file, and import it into your index.js file.

const store = createStore(yourReducerGoesHere)

Simple enough? Now your store exists in a variable called store. It takes in a reducer as well.(This is how it will manipulate the state that's held within the store. Now, let's talk about the Provider.

Providing state to your components

Provider is simple enough to remember, because it provides access the state from the store to your components. I say access, because it doesn't necessarily give your components the state just yet (this is what we have connect() for).

In that same component, you'll want to import Provider.

import { Provider } from 'react-redux' Booyah!

After that, you want to wrap your App component in that provider. Think of this as granting your application the ability to use the store. It typically looks something like this:

ReactDOM.render(
<Provider store={store}>
    <App />
</Provider>
, document.getElementById("root"));

See that sneaky little prop pass, right there? It almost forms a sentence! In the Provider we passed in the store. It can almost be read as, "Providing the store to the component". Well, that's how I read it at least! :)

Awesome, now we created a store, passed the store to the provider, which is providing that store to our application. Before seeing how we grab the state, we need to have state first! On to the reducer!

Reducing The Stress

Reducers! This is one of the powerful aspects of Redux. Essentially, I call them the execution guidelines. The reducer file will typically consist of two things: the initial state, and the reducer itself.

For example, for simplicity sake, let's say our initial state has an array of names.

const initialState = {
   names: ['Bob', 'Susan']
}

Woo! They are looking great. Now the reducer comes into play. This section can get messy, so we'll keep it extremely simple. Reducers are functions full of if...else conditions. The easier way to write this is with switch cases. To prevent confusion, I'll provide an example of both, if...else and a switch case, if you happen to be familiar with both!

Our case that modifies state will be called, 'Add Name'. However, in Redux cases, it's common practice to use all capital letters for this (kind of similar to just screaming at the reducer to do its job), so it would look like 'ADD_NAME'.

If none of the cases do match, you want to be sure to return the initialState. I know this is a lot of words, so let's see an example!

export const reducer = (state = initialState, action) => {
    if (action.type === 'ADD_NAME') {
        return {
            ...state,
            names: [...state.names, action.payload]
        }
    } else {
        return state
    }
}

What's happening here is the reducer takes in state, and an action. State will be undefined if you don't provide it an initial state, so in this example, we assign state to initialState. The action will be an object containing a type and sometimes a payload property. For example, this action object for this example may look like:

{ type: 'ADD_NAME', payload: newNameGoesHere }

The type specifies what reducer case to trigger, like instructions! The payload is just data, it can be called anything. In this case, we have a new name we want to add to the users array. So we spread the whole state object first, then spread the users array into a new array, and add the new name on to the end, this name is being referenced by the action.payload.

So back to my point, reducers are the execution guidelines. They take instruction from the action, and perform based on what action.type is called. This will make more sense in a second when we discuss actions. The payload property is just a common way of passing in the data you want to incorporate into state, it can be called anything - beanChili if you want! :D

Like I said, reducers are typically written in a switch case format, so they may look like this when you come across them:

export const reducer = (state = initialState, action) => {
    switch(action.type){
        case 'ADD_NAME':
            return {
                ...state,
                names: [...state.names, action.payload]
            }
        default:
            return state
    }
}

This achieves the same result, just tends to be less words, the longer your code gets!

Okay, so we've covered the store, the provider, initial state, and the reducer. Now let's take a peek at actions!

Lights, Camera, ACTIONS

As I stated earlier, actions are the instructions for the reducer. Action creators are functions, that return actions. These actions are objects similar to the one I referenced above, with a type and a payload property.

The way these work, is your action creator function is called within your component, which returns an object of "instructions". In this case, you call the action, and it will return an object that looks like:

{ type: 'ADD_NAME', payload: newName }

This function could be represented by:

export const addName = (newName) => {
   return { type: 'ADD_NAME', payload: newName }
}

In this case, when the addName function is invoked, we will pass in the name we want to add, as newName!

Now, this returned object gets passed into the reducer. Can you tell what's going to happen?

The reducer enters the switch case, and checks the action.type. OH! The type is 'ADD_NAME', so hop into that return statement.

Okay, so it is returning state, and then attaching action.payload onto the enter of the array... what is action.payload?

Well, referencing our object above, we see action.payload is the newName. Let's say that we passed in the name 'Chris' as the newName argument. What happens now, is Chris is tacked onto the end of the array. Now our users array in state looks like:

['Bob', 'Susan', 'Chris'] Awesome!

So essentially we just called a function (an action creator), which said, "Hey Reducer... add a new name, the new name is Chris!"

The reducer responds, "Cool, added the name, here's your new state!"

Simple enough, right? They definitely get more complex as more functionality is incorporated into your application, but these are the basics.

However, there is one final question:

How do the components actually access this state?

Simple! By connect! Let's take a look.

Connecting the links

Connecting the store state to our components becomes a bit of extra work, but essentially we have our state, and provide access to the main component (App.js). However, now we need to accept access, via the connect() method.

Connect is a higher-order component, which is a different topic itself, but essentially this gets invoked twice in a row. It is called during the export of your component.

First, let's import connect into our component:

import { connect } from 'react-redux';

Say we have a <List /> component being rendered in App.js, and we want to connect List.js. In that component, on the export line we could do something like:

export default connect(null, {})(List);

The first invocation takes in two items, the state you're receiving, and the actions you want to use (in that order). Let's touch on the state.

Remember, connecting only accepts access, it doesn't actually provide the state, that's what we have mapStateToProps for. :D

mapStateToProps says, "Oh, you connected your component? You granted access? Well here is the state you asked for!"

Okay... Maybe the component doesn't talk, but if they did, they'd probably say something along those lines.

This mapStateToProps example, is a function that receives the state, and is then passed into the connect method. Like this:

const mapStateToProps = state => {
   return {
      names: state.names 
   }
}

This function takes in state, which is the entire state object from the reducer. In this case, our state object only has one array inside of it, but these state objects are typically 10x as long, so we have to specify what information we want!

In this return line, we say, "Return an object with a names property." How do we know what names is? Well, we access it off of the state object, by state.names.

Our returned property doesn't need to be called names, we could do something like:

const mapStateToProps = state => {
   return {
      gummyBears: state.names
   }
}

But, that's not very semantic is it? We want to understand that names is an array of names. So it's common practice to keep the same property name, in your returned state object!

We're almost finished, so hang in there! Let's recap where we're at.

We have our component accessing state from the store, through mapStateToProps. The state exists in the component now, but the component can't access it just yet.

First, we need to pass it to the connect function. The connect functions says, "Access to the store granted! Now... what state am I granting access to?"

So we pass in the function returning state, mapStateToProps, like this:

export default connect(mapStateToProps, {})(List) Radical!

We're almost there!

Now the component is capable of receiving that state as props, like it traditionally would from a parent component. Maybe we are mapping over it, and displaying each name on the screen in a div. Here's what this may look like!

const List = props => {
    return (
        <div>
            {
                props.names.map(name => {
                    return <div>{name}</div>
                })
            }
        </div>
    )
}

Awesome! But there is one final problem... Where does the action get called?

Typically there would be an input, so you could input a new name, and add it to the array - but, for simplicity sake, let's just add a button that adds the name Chris, when clicked! (Not very functional, but you see my point! :D)

We need to access that action creator function. Well, earlier we exported that function so we could import it where we need it, like in our List.js component!

import { addName } from "../actions"

The file location will depend on your directory structure, but it is common to have all actions exported from an index.js file in your actions directory, and then import from that directory. Don't worry too much about that now though!

Great, we have our function, but we can't just pass this function as props to our component just yet. This action is related to Redux, and with Redux we need to connect the action through the connect higher-order component, so when we return our action object, our reducer can accept it and perform accordingly!

Remember that extra space in the connect at the bottom of our List.js component? Let's fill that in with our addName function.

export default connect(mapStateToProps, {addName})(List);

Now, we can pass in our function as props (similar to our state), and use the function as we need!

const List = props => {
    return (
        <div>
            <button onClick={() => props.addName('Chris')}></button>
            {
                props.names.map(name => {
                    return <div>{name}</div>
                })
            }
        </div>
    )
}

I simply created a button, and added an onClick event listener, which triggers the addName function, and passing in 'Chris', like we set out to achieve!

Geez! that was a mission... but we made it! So, let's recap what is happening exactly.

The Redux Recap

We started with creating our store, and passed access to it through the provider, which wrapped our application. Then we created our initial state to use, and formed our reducer which manipulates the state. We built an action creator, addName which is a function that returns instructions for the reducer. These specific instructions said, "We want to add the name Chris to the names array!"

The reducer then takes that information and adds the name to the state. Our component accesses the state through connect, and receives the state through the mapStateToPropsfunction. We also import our action creator, addName, and pass it to connect as well.

The result? We can access our action creator, and our state, as props! However, we aren't passing this information through any other components, just pulling it directly from the store. Delivery straight to your door! Uber eats roc- I mean, Redux rocks!

I understand there is so much more to Redux, and many other things you can change to make everything easier and simpler to use, I just wanted to cover some of the basic foundations of it, and what has helped me understand it a bit better!

I would love to hear your thoughts/opinions on Redux, and your experience with it, in the comments. I love talking about React + Redux! :D

Discussion

pic
Editor guide
Collapse
ebnbatran profile image
Ebrahim Batran

Often when instructors try to explain Redux they don't put themselves in the beginner's shoes, or they don't as they should be.

This article is beautiful because just that!
It went through the logical steps a beginner's mind goes through when trying to learn Redux. And this is exactly what makes it super useful.

As always Dylan you know how to simplify and express the JS!

Collapse
dylanmesty profile image
Dylan Mestyanek Author

Ebrahim, you rock man! I'm so glad you got some value from it. Trying my best, and I'm glad you really enjoyed it! Happy Sunday, and happy coding! 💪🏻🔥

Collapse
dcsan profile image
dc

Thanks, quite clearly written up to dumuddle some of the layers of boilerplate redux forces you into.

However you don't go the extra step of showing how redux is useful - actually modifying values or retrieving data from a server.

Since we're passing in props how does redux reactively update state ?
Where would you put code to do GET requests?

Also I'm unclear of the value of the extra layer of abstraction around actions, they're just wrapper functions for calling reducer methods, why not name and add methods to the reducer and call them directly?

Collapse
dylanmesty profile image
Dylan Mestyanek Author

Great point, totally makes sense! I wanted to refrain from getting to in depth with the possibilities with Redux, because there's a TON to go over. I just wanted to introduce the basic idea of actions and reducers to anyone who has been struggling to grasp them! I most definitely plan to go a bit more in depth to cover various situations, such as the GET requests.

Thanks for the suggestions, I'll be sure to dive a bit deeper and offer examples of various situations to provide a better grasp around the concepts! :)

Collapse
dcsan profile image
dc

thanks for the response. I'll surely look out for the next installment!

Collapse
sherribooher profile image
Sherri Booher

Dylan, thank you, thank you, thank you! I am also a student in a full stack web development bootcamp and Redux was something I just couldn't grasp, at least not in the way it was taught to me. This article helped me understand exactly HOW Redux works and with that information, I am now able to use it more confidently. I think you should think about becoming an instructor. You have a wonderful teaching style...I think you would be one of the best! Again, thank you for this!

Collapse
dylanmesty profile image
Dylan Mestyanek Author

Sherri that means the world! Thanks so much! While I definitely have a long... LONG way to go, I appreciate the kind words greatly! Super stoked to be able to provide some value to you, and help you grasp the Redux basics a bit more. That's awesome hear! Thanks so much! :)

Collapse
favreleandro profile image
Leandro Favre

Good article! Just a point:

here you did:

export const reducer = (state = initialState, action) {
switch(action.type){
case 'ADD_NAME':
return {
...state,
names: [...names, action.payload]
}
default:
return state
}
}

But in the return of ADD_NAME, must be

names: [...state.names, action.payload]

the same in if...else structure.

Collapse
dylanmesty profile image
Dylan Mestyanek Author

Thank you so much for the correction, you're most definitely right! Slipped my mind - I got those changed in the article. I appreciate that, thanks again! :)

Collapse
favreleandro profile image
Leandro Favre

A pleasure! Thanks for sharing your article!

Collapse
shmdhussain12 profile image
Mohamed Hussain

I am beginner in Redux, trying to learn, recently I tried to read about redux, then after reading only part of any article about redux , just stopped as there is too much info, Good here is I am able to read the complete article of you, such a clear and simple explanation of redux terms and basic concept.

One doubt, In reducer actions, why we need to spread the state along with the names[] array, (I am thinking we need only the updated state i.e names only )

return {
...state,
names: [...names, action.payload]
}

Collapse
dylanmesty profile image
Dylan Mestyanek Author

I'm glad you enjoyed the read! Thank you!

So the reason we have to return ...state, as well, is because what you're returning is taking place of your current state. In this case, names is the only thing in state, so it doesn't really matter.

However, say we had another array with ages, or an object in state, in addition to the names array, we'd have to be sure to copy everything from State with ...state, and then change what we need to change.

If there was another array called ages, and the names array, and we only returned names, then ages would be overridden and be gone forever. If that makes sense. :)

Collapse
sanjeevkse profile image
sanjeev shetty

Thank youu Dylan, You made my day into Redux, I was just searching for articles on redux. I could not find more easier than yours.

People feels they themselves are into this redux world along with you in story telling. Thats a great skill you do have.

Hopefully I also start writing like this in coming days.

Collapse
niweera profile image
Nipuna Weerasekara

I've been using Redux for almost a year now. But never have I ever come across such a beautiful explanation... Awesome mate. Keep up the good work...

Collapse
dylanmesty profile image
Dylan Mestyanek Author

This means the world! Super glad it was easy to understand for you, that's super special to me! Glad you enjoyed the read! :)

Collapse
jacobwritescode profile image
Jacob James

This is the simplest way to make others understand what Redux is. Really loved it brother. You really simplified Redux.

Collapse
dylanmesty profile image
Dylan Mestyanek Author

Thanks Jacob! That means the world, super stoked to hear that it clicked with you. Appreciate your support, have a great Sunday! 😄

Collapse
ashwalls93 profile image
wallsy93

Very nice blog post! I'm a strong believer in explaining things using language anyone can understand and this post does just that!

Collapse
dylanmesty profile image
Dylan Mestyanek Author

Awesome! Thanks so much! That was definitely the goal to break it down to a level that was easier to understand. Glad it provided you some value!😄

Collapse
aregbesolaoj profile image
Aregbesola John

Thank you Dylan....this is pretty clean and 'lightweight' to understand 😊👍🏼

Collapse
nugbug profile image
Joshua Tanguay

I'm currently taking a course on React+Redux and this article explained it so much better for my n00b brain than said course. Thanks for posting this! Great article!

Collapse
lifenautjoe profile image
Joel Hernández

Redux is such an over-complication but hey, its Facebook tech right? Should be good right? 🤦‍♂️

Collapse
13nidhi profile image
13nidhi

thank you thank you thank you...... so much for a great explanation...

Collapse
j523 profile image
Vidisha Parab

This is awesome ! many thanks :)