Easy introduction to Redux

An easy and fun explanation about Redux basics!

latrova commited 2 years, 1 month ago

You probably have find lots of tutorials about React + Redux, but none of them explaining it in a simple way, right to the point nor why you should use it and which advantages it brings to your project... Until now!

If you, like me, after following such tutorials find youself a little lost...

Get ready to understand everything as easy as I can write right now!

First things first

You need to have some basic knowledge about React and REST API. Is also required understanding about a component, how state and props works.

What you will find here

  • Basic explanation about how Redux works
  • Example of scenario where Redux suits well
  • Basic elements that composes Redux

What you will not find here

  • How to integrate Redux into your project
  • How to create a project with Redux
  • An example project

Now we're good to go.

Quick Facts

  • Redux is not part of React, nor is exclusive, you can even use Redux with Angular 2 if you want to
  • You can create an entire application without Redux
  • Redux is just a module that stores a global state for your application

Scenario

Let's imagine that you owns a system that lists some sales and customers. So, you got 2 components:

SalesTable and CustomersTable

Both of them requests data from api.awesome-sales.com in customers and sales endpoints, which answers as follows

/customers

[
   {
       "id": 1,
       "name": "Jhon Doe",
   },
   ...
]

/sales

[
   {
      "id": 32,
      "date": "2017-07-17",
      "total_cost": 52.99,
      "customer_id": 1 
   },
   ....
]

Finally, you want your CustomersTable to displays as follows:

+----+------------+--------------+-------------+
| Id | Date | Sold to | Total |
+----+------------+--------------+-------------+
| 32 | 07/17/2017 | Jhon Doe | $ 52,99 |
+----------------------------------------------+

The issue

Note that our API doesn't brings customer name, just its id. Before we start discussing some solution, I would like to add a constraint: You can consume API, but not change it. What to do?

Send requests to customers/:id?

NO! Imagine that we got 50 sales and for each of them we have a different customer, it would result in 50 unneeded requests for our API.

Right, what to do then?

I'll give one more information: Before user even touches SalesTable, he opens CustomersTable. If it's true, we can guarantee that our desired information are stored in CustomersTable state.

So what we need is to keep a state to share across all application where several components can have access to common data. A thing that Redux already handles very well while maintaining such state and injecting it through props into components.

Which can be pictured in following way

Although this article's main objective is not teach Redux, I think that's ok to comment and see a little bit about its magic.

Redux is composed by 3 elements (We can count 6 if we add Store, Provider and Middleware)

  • Actions
  • Actions Creators
  • Reducers

Actions

A name that describes an action. Using scenario cited above we can imagine some possible actions:

  • FETCH_SALES
  • FETCH_CUSTOMERS
  • SAVE_CUSTOMERS
  • SAVE_SALE
  • [Others]

It looks easy, right? That's because it really is. An action represents just a name/dumb payload, not more. It only notifies Redux that something happened, but doesn't change anything by itself.

Talk between an Action and Redux

Action: Hey, Redux! I'll ask for customers from API! Redux: Ok :) Action: Hey, Redux! I got those customers. Here they are (delivers an array) Redux: Ok :)

See? I told you it was easy.

Actions Creators

You probably will mistake action for action creator sometime, just because they are strictly related and have similar names. It's only purpose is to help creating an action correctly.

const RECEIVE_CUSTOMERS = "RECEIVE_CUSTOMERS"; //Action

const receiveCustomers = (customers) => {
    return {
        type: RECEIVE_CUSTOMERS,
        payload: customers
    }
}

An action creator may get complex when handling an API request since an action may dispatch other actions. I borrowed and adapted an example from Redux Docs to show a case like that.

  return function (dispatch) {
        dispatch(requestPosts(subreddit)) //First action

    return fetch(`https://www.reddit.com/r/${subreddit}.json`)
      .then(
        response => response.json(),
        error => dispatch(receivePostsFailed(error) //Another action
      )
      .then(json =>
        dispatch(receivePosts(subreddit, json)) //Another action
      )
  }
}

Note that always are dispatched at least 2 actions. First one notifies application that there's a pending request, then second action notifies the result (regardless if successful or not).

Reducers

It's up to Reducer define the new global state. It's basically a function that receives previous state, dispatched action and returns the new state.

reducer(prevState, action) {
    ...
    return newState;
}

Conclusion

Finally, based on what we've seen so far, we can affirm: A component dispatches an action with action creator's help to Redux. Redux itself sends the action to reducer which updates Redux global state what allows Redux to inject its new state into components props.

And that's all, folks! Don't forget to drop a comment below with your thoughts about this post format. You can point If I missed something. Suggestions and improvements are very welcome.

Oh! And since english is not my first language, you may have found some errors above, don't forget to let me know so I can fix them!

And now?

If you're still interested in reading more about Redux, I would like to suggest:

  • Redux Docs
  • Getting Started With Redux - Course (from Redux creator) explaining how to create an application with Redux and pointing some news about ES6 and good practices
  • Ducks - An architecture proposal to organize and manage your Redux elements
blog comments powered by Disqus