Intro to Redux For Web Developers

MyCodingLife
So yah, this is how we are going to code our next website, mmkay?

Time for another blog post!  Today, let’s talk about Redux, which is a JavaScript library that is used to manage and update data (aka state) in most commonly React applications.

React is very good at building components that house HTML, CSS and individual JavaScript manipulations that can be reused again in other areas of code.  React also uses “state” to help components re-render themselves after data within the component has changed, perhaps through a JavaScript event — e.g. a div went from “hidden” state to “show” state, or if a search box was typed into, the state for that component must now update and “fetch” results to show under the search box, and so on.  

At face value, this rendering and re-rendering of components based on state changes is very nice.   The fact that re-renders happen so quickly and easily is a huge selling point for using React on any project.

Real World

This is Okay

However, there is a slight problem with using components to manage their own state.   As a project scales in complexity, so does the responsibility of React and its components to keep up. 

For example, let’s say I made a really cool todo list that shows items as “to do” and “completed”.   This todo list component can be neatly dropped into a React component, with its own internal state that can be reused across multiple applications.  

But what if I wanted to add more features, like “dark mode” and “light mode”?  How about the ability to “undo” and “redo” additions or deletions from the list?  How about “copy” items in the list or “select all”?

Getting Complex

As you can tell, this would mean a whole lot more changes to the original todo list component, and ultimately that component would be managing way more state changes.   On top of that, what if as we added these new features, some people got mad and wanted the “old todo list” instead?

Would we scrap all our hard work?  Or would we make two different versions of components with two different versions of list-management?   What if we had a third group of people wanting some features but not all?  How would we build for that group as well?

Projects Scale in Complexity.

One thing that is typical of web development (and software engineering in general) is that projects SCALE in complexity!

Enter Redux 

Rather than have state be managed by individual components, your state moves up to a “central store” that is outside of your App entirely.  You can think of this store as the “brains of the operation”.  If you read my previous post on MVC, then you could think of Redux as another method of system design to help manage interactions between data and application.  

The app then is responsible only for rendering and re-rendering itself according to how the state in the “store” changes.   There are no calculations to be made on the component side, rather components dispatch ACTIONS to the store, that get interpreted by store logic and REDUCED into new state that the app is subscribed to.  On changes to state, the app re-renders.

This separation between app and store allows for components to subscribe to state they need rather than an entire state tree, and re-render only what they are concerned with. In addition, we no longer need to hunt through dozens of components to amend or update logic, since it is handled all in once place.  Awesome!

Redux vs MVC

Some people ask what’s the difference between MVC and Redux?  The key difference is the way that data flows with an app.  In a typical MVC application, data is sent back and forth via controllers (and sometimes manipulated at the view level).  This often leads to the same problems that components have– state is managed by the component, aka controller, so more controllers are necessary when scaling an application, which becomes difficult to develop and debug for.   Redux has all changes instead sent as actions, which essentially forces one-way data flow (data only comes from the M in new state) as opposed to two way data in traditional MVC.   This makes an application easier to track and understand, and possibly better for overall development.

Try It Out

If you want to try learning Redux, you can get a template from React via the Create React App Script.  Use the command:

npx create-react-app <my-project-name> --template redux

Doing so will download a project to your computer that you can begin looking through.  Remember to look in the src folder, as all React projects start there.  At the time of writing, you should see a folder for a counter project, which you can explore.   You can run this app by using the command npm start or yarn start (preferred).

Alternatively, you can also just download redux as a dependency for your own project with yarn add redux or npm install redux.

Some Redux Concepts

It’s helpful to know some of the vocabulary with Redux especially as you start diving in.  Let’s review the terms:

The Store

This is your one central area for managing your state.  You initialize a data store for your app by writing import {createStore} from "redux" near the top of your App.js file, and then creating a local store variable, e.g. let store = createStore(<reducer>).  The <reducer> is a function you supply to createStore that will serve as the logic gate for interpretting any actions that come to the store.   The store variable also gives you access to state via store.getState() which returns the current state of the application.

Actions

State is typically undefined or set to a base primitive value, but it’s through actions that we can shape how the state of an application changes.  Actions are just simple JavaScript objects that tell the reducer in your store what to do with its internal state.    Actions are sent to the store via store.dispatch({type: 'MY ACTION'}).

Reducers

Reducers are functions that the store uses to interpret incoming actions.  Typically you’ll see if/else statements inside reducers or switch statements.  For example, if a reducer gets an action to 'ADD' to its state, then the reducer will carry out that action and return a new state that has been added to.   If the action is subtract, then the reducer will return a new state that has been subtracted from.  

As a caveat, reducers should only be used to manipulate and create new state.  They should not perform any extra work (e.g. like DOM manipulation, server-side requests).  Redux states that reducers should be pure functions and only manage state, any other “side effects” should go to separate functions instead.

CodeSandbox

Tying it altogether, I put together a codesandbox you can browse here and play around with (no need to install anything).

The codesandbox imports createStore from redux, then sets up a local store variable tied to my calculate reducer function.   Inside the reducer function, I have a switch statement that looks at incoming actions and generates new states accordingly — we’ll come back to this in a second.

My app is concerned with just rendering the data passed from the store.  I get state from the store via store.getState() and then save it in a local variable called currentValue (which is a state of 0 by default).  

I have set up my component so that if state changes, we are subscribed to the changes via store.subscribe(() => this.forceUpdate()) inside my componentDidMount method, which just basically says that if my store changes, execute the function inside that forces a re-render of this component.  That will then get the new state again from the store and output my new state as currentValue which you can see in the paragraph tag area “The current value is {currentvalue}”.

The buttons on my page dispatch actions on click.  For example, if a user clicks the button 'ADD', then an action is dispatched via store.dispatch({type: 'ADD'}).  My store then reduces this action down into a new state which is the old state + 1.   Similarly if I push the button 'SUBTRACT', then an action for subtract is called which returns a new state of old state – 1.   For any actions not understood, the reducer just returns the current state.  

In this way, my logic for my application is separated from my app, and different buttons can be dialed into different actions (as well as different components can be dialed into different logic).   We’ll see more of that in a successive tutorial.

Stay Tuned!

For now, hope you enjoyed this beginner tutorial!  Stay tuned for more blog updates, and if you haven’t already, get on our mailing list so you can be informed about new blogs, video tutorials as well as new meetups with our group (which happen monthly).  Be sure to follow us on social media as well, especially Youtube.com/JavaScriptLA, where you can get video tutorials and watch the past meetups too (handy if you missed a lecture).  Level up your programming skills with us!  See you at the next meetup!

Vijay

Organizer