Top Level Control with Redux State Management: A ClojureScript Tutorial

Top Level Control with Redux State Management: A ClojureScript Tutorial

ClojureScript is the tool of choice for front-end developers who have tried it. Recently in this series, we showed how to use it to get started with React. In today’s tutorial, Toptal Freelance Clojure Developer Luke Tomlin dives into how to use Redux for React state management in ClojureScript.

Welcome back for the second exciting installment of Unearthing ClojureScript! In this post, I’m going to cover the next big step for getting serious with ClojureScript: state management—in this case, using React.

With front-end software, state management is a big deal. Out-of-the-box, there are a couple ways to handle state in React:

  • Keeping state at the top level, and passing it (or handlers for a particular piece of state) down to child components.
  • Throwing purity out of the window and having global variables or some Lovecraftian form of dependency injection.

Generally speaking, neither of these are great. Keeping state at the top level is fairly simple, but then there’s a large amount of overhead to passing down application state to every component that needs it.

By comparison, having global variables (or other naive versions of state) can result in hard-to-trace concurrency issues, leading to components not updating when you expect them to, or vice versa.

So how can this be tackled? For those of you who are familiar with React, you may have tried out Redux, a state container for JavaScript apps. You may have found this out of your own volition, boldly searching for a manageable system for maintaining state. Or you might have just stumbled across it while reading about JavaScript and other web tooling.

Regardless of how people end up looking at Redux, in my experience they generally end up with two thoughts:

  • “I feel like I have to use this because everyone says that I have to use it.”
  • “I don’t really fully understand why this is better.”

Generally speaking, Redux provides an abstraction that lets state management fit within the reactive nature of React. By offloading all of the statefulness to a system like Redux, you preserve the purity of React. Thus you’ll end up with a lot less headaches and generally something that’s a lot easier to reason about.


For Those New to Clojure

While this may not help you learn ClojureScript entirely from scratch, here I will at least recap some basic state concepts in Clojure[Script]. Feel free to skip these parts if you’re already a seasoned Clojurian!

Recall one of the Clojure basics that applies to ClojureScript as well: By default, data is immutable. This is great for developing and having guarantees that what you create at timestep N is still the same at timestep > N. ClojureScript also provides us with a convenient way to have mutable state if we need it, via the atom concept.

An atom in ClojureScript is very similar to an AtomicReference in Java: It provides a new object that locks its contents with concurrency guarantees. Just like in Java, you can place anything you like in this object—from then on, that atom will be an atomic reference to whatever you want.

Once you have your atom, you can atomically set a new value into it by using the reset! function (note the ! in the function—in the Clojure language this is often used to signify that an operation is stateful or impure).

Also note that—unlike Java—Clojure doesn’t care what you put into your atom. It could be a string, a list, or an object. Dynamic typing, baby!

(def my-mutable-map (atom {})) ; recall that {} means an empty map in Clojure

(println @my-mutable-map) ; You 'dereference' an atom using @
; -> this prints {}

(reset! my-mutable-map {:hello "there"}) ; atomically set the atom
(reset! my-mutable-map "hello, there!") ; don't forget Clojure is dynamic :)

Reagent extends this concept of an atom with its own atom. (If you’re not familiar with Reagent, check out the post before this.) This behaves identically to the ClojureScript atom, except it also triggers render events in Reagent, just like React’s in-built state store.

An example:

(ns example
(:require [reagent.core :refer [atom]])) ; in this module, atom now refers
; to reagent's atom.

(def my-atom (atom "world!"))

(defn component
[]
[:div
[:span "Hello, " @my-atom]
[:input {:type "button"
:value "Press Me!"
:on-click #(reset! My-atom "there!")}]])

This will show a single <div> containing a <span> saying “Hello, world!” and a button, as you might expect. Pressing that button will atomically mutate my-atom to contain "there!". That will trigger a redraw of the component, resulting in the span saying “Hello, there!” instead.

This seems simple enough for local, component-level mutation, but what if we have a more complicated application that has multiple levels of abstraction? Or if we need to share common state between multiple sub-components, and their sub-components?


A More Complicated Example

Let’s explore this with an example. Here we will be implementing a crude login page:

(ns unearthing-clojurescript.login
(:require [reagent.core :as reagent :refer [atom]]))

;; -- STATE --

(def username (atom nil))
(def password (atom nil))

;; -- VIEW --

(defn component
[on-login]
[:div
[:b "Username"]
[:input {:type "text"
:value @username
:on-change #(reset! username (-> % .-target .-value))}]
[:b "Password"]
[:input {:type "password"
:value @password
:on-change #(reset! password (-> % .-target .-value))}]
[:input {:type "button"
:value "Login!"
:on-click #(on-login @username @password)}]])

We will then host this login component within our main app.cljs, like so:

(ns unearthing-clojurescript.app
(:require [unearthing-clojurescript.login :as login]))

;; -- STATE

(def token (atom nil))

;; -- LOGIC --

(defn- do-login-io
[username password]
(let [t (complicated-io-login-operation username password)]
(reset! token t)))

;; -- VIEW --

(defn component
[]
[:div
[login/component do-login-io]])

The expected workflow is thus:

  1. We wait for the user to enter their username and password and hit submit.
  2. This will trigger our do-login-io function in the parent component.
  3. The do-login-io function does some I/O operation (such as logging in on a server and retrieving a token).

If this operation is blocking, then we’re already in a heap of trouble, as our application is frozen—if it’s not, then we have async to worry about!

Additionally, now we need to provide this token to all of our sub-components that want to do queries to our server. Code refactoring just got a lot harder!

Finally, our component is now no longer purely reactive—it is now complicit in managing the state of the rest of the application, triggering I/O and generally being a bit of nuisance.


ClojureScript Tutorial: Enter Redux

Redux is the magic wand that makes all of your state-based dreams come true. Properly implemented, it provides a state-sharing abstraction that is safe, fast, and easy to use.

The inner workings of Redux (and the theory behind it) are somewhat outside the scope of this article. Instead, I will dive into a working example with ClojureScript, which should hopefully go some way to demonstrating what it’s capable of!

In our context, Redux is implemented by one of the many ClojureScript libraries available; this one called re-frame. It provides a Clojure-ified wrapper around Redux which (in my opinion) makes it an absolute delight to use.


The Basics

Redux hoists out your application state, leaving your components lightweight. A Reduxified component only needs to think about:

  • What it looks like
  • What data it consumes
  • What events it triggers

The rest is handled behind the scenes.

To emphasize this point, let’s Reduxify our login page above.


The Database

First things first: We need to decide what our application model is going to look like. We do this by defining the shape of our data, data which will be accessible throughout the app.

A good rule of thumb is that if the data needs to be used across multiple Redux components, or needs to be long lived (like our token will be), then it should be stored in the database. By contrast, if the data is local to the component (such as our username and password fields) then it should live as local component state and not be stored in the database.

Let’s create our database boilerplate and spec out our token:

(ns unearthing-clojurescript.state.db
(:require [cljs.spec.alpha :as s]
[re-frame.core :as re-frame]))

(s/def ::token string?)
(s/def ::db (s/keys :opt-un [::token]))

(def default-db
{:token nil})

There are a few interesting points worth noting here:

  • We use Clojure’s spec library to describe how our data is supposed to look. This is especially appropriate in a dynamic language like Clojure[Script].
  • For this example, we’re only keeping track of a global token that will represent our user once they have logged in. This token is a simple string.
  • However, before the user logs in, we won’t have a token. This is represented by the :opt-un keyword, which stands for “optional, unqualified.” (In Clojure, a regular keyword would be something like :cat, while a qualified keyword might be something like :animal/cat. Qualifying normally takes place at the module level—this stops keywords in different modules from clobbering each other.)
  • Finally, we specify the default state of our database, which is how it is initialized.

At any point in time, we should be confident that the data in our database matches our spec here.


Subscriptions

Now that we have described our data model, we need to reflect how our view shows that data. We have already described what our view looks like in our Redux component—now we simply need to connect our view to our database.

With Redux, we do not access our database directly—this could result in lifecycle and concurrency issues. Instead, we register our relationship with a facet of the database through subscriptions.

A subscription tells re-frame (and Reagent) that we depend on a part of the database, and if that part is altered, then our Redux component should be re-rendered.

Subscriptions are very simple to define:

(ns unearthing-clojurescript.state.subs
(:require [re-frame.core :refer [reg-sub]]))

(reg-sub
:token ; <- the name of the subscription
(fn [{:keys [token] :as db} _] ; first argument is the database, second argument is any
token)) ; args passed to the subscribe function (not used here)

Here, we register a single subscription—to the token itself. A subscription is simply the name of the subscription, and the function that extracts that item from the database. We can do whatever we want to that value, and mutate the view as much as we like here; however, in this case, we’re simply extracting the token from the database and returning it.

There is much, much more you can do with subscriptions—such as defining views on subsections of the database for a tighter scope on re-rendering—but we’ll keep it simple for now!


Events

We have our database, and we have our view into the database. Now we need to trigger some events! In this example, we have two kinds of events:

  • The pure event (having no side effect) of writing a new token into the database.
  • The I/O event (having a side effect) of going out and requesting our token through some client interaction.

We’ll start with the easy one. Re-frame even provides a function exactly for this kind of event:

(ns unearthing-clojurescript.state.events
(:require [re-frame.core :refer [reg-event-db reg-event-fx reg-fx] :as rf]
[unearthing-clojurescript.state.db :refer [default-db]]))

; our start up event that initialises the database.
; we'll trigger this in our core.cljs
(reg-event-db
:initialise-db
(fn [_ _]
default-db))

; a simple event that places a token in the database
(reg-event-db
:store-login
(fn [db [_ token]]
(assoc db :token token)))

Again, it’s pretty straightforward here—we’ve defined two events. The first is for initializing our database. (See how it ignores both of its arguments? We always initialize the database with our default-db!) The second is for storing our token once we’ve got it.

Notice that neither of these events have side effects—no external calls, no I/O at all! This is very important to preserve the sanctity of the holy Redux process. Do not make it impure lest you wish the wrath of Redux upon you.

Finally, we need our login event. We’ll place it under the others:

(reg-event-fx
:login
(fn [{:keys [db]} [_ credentials]]
{:request-token credentials}))

(reg-fx
:request-token
(fn [{:keys [username password]}]
(let [token (complicated-io-login-operation username password)]
(rf/dispatch [:store-login token]))))

The reg-event-fx function is largely similar to reg-event-db, although there are some subtle differences.

  • The first argument is no longer just the database itself. It contains a multitude of other things that you can use for managing application state.
  • The second argument is much like in reg-event-db.
  • Rather than just returning the new db, we instead return a map which represents all of the effects (“fx”) that should happen for this event. In this case, we simply call the :request-token effect, which is defined below. One of the other valid effects is :dispatch, which simply calls another event.

Once our effect has been dispatched, our :request-token effect is called, which performs our long-running-I/O login operation. Once this is finished, it happily dispatches the result back into the event loop, thus completing the cycle!


ClojureScript Tutorial: The Final Result

So! We have defined our storage abstraction. What does the component look like now?

(ns unearthing-clojurescript.login
(:require [reagent.core :as reagent :refer [atom]]
[re-frame.core :as rf]))

;; -- STATE --

(def username (atom nil))
(def password (atom nil))

;; -- VIEW --

(defn component
[]
[:div
[:b "Username"]
[:input {:type "text"
:value @username
:on-change #(reset! username (-> % .-target .-value))}]
[:b "Password"]
[:input {:type "password"
:value @password
:on-change #(reset! password (-> % .-target .-value))}]
[:input {:type "button"
:value "Login!"
:on-click #(rf/dispatch [:login {:username @username
:password @password]})}]])

And our app component:

(ns unearthing-clojurescript.app
(:require [unearthing-clojurescript.login :as login]))

;; -- VIEW --

(defn component
[]
[:div
[login/component]])

And finally, accessing our token in some remote component is as simple as:

(let [token @(rf/subscribe [:token])]
; ...
)

Putting it all together:

No fuss, no muss.


Decoupling Components with Redux/Re-frame Means Clean State Management

Using Redux (via re-frame), we successfully decoupled our view components from the mess of state handling. Extending our state abstraction is now a piece of cake!

Redux in ClojureScript really is that easy—you have no excuse not to give it a try.

If you’re ready to get cracking, I’d recommend checking out the fantastic re-frame docs and our simple worked example. I look forward to reading your comments on this ClojureScript tutorial below. Best of luck!

Related:

[State Management in Angular Using

Firebase](https://www.toptal.com/angular/state-management-in-angular-using-firebase)


Understanding the Basics

What is a Redux state?

The Redux state refers to the single store that Redux uses to manage the application state. This store is solely controlled by Redux and is not directly accessible from the application itself.

Is Redux event sourcing?

No, Redux is a separate technology from the pattern known as event sourcing. Redux was inspired by another technology called Flux.

What is a Redux container?

A Redux container (or simply a “container”) is a React component that subscribes to the Redux state, receiving updates when that part of the state changes.

Is Redux a framework?

Yes, Redux provides a framework around state management in a web application.

What is ClojureScript?

ClojureScript is a compiler for Clojure that targets JavaScript. It is commonly used to build web applications and libraries using the Clojure language.

Originally published at https://www.toptal.com

Learn More

Modern React with Redux

Become a JavaScript developer - Learn (React, Node,Angular)

The Complete React Web Developer Course (2nd Edition)

Node with React: Fullstack Web Development

Beginner Full Stack Web Development: HTML, CSS, React & Node



How to Implement Redux in 24 Lines of JavaScript?

How to Implement Redux in 24 Lines of JavaScript?

Redux is among the most important JavaScript libraries ever created. In this posr, you'll learn how to Implement Redux in 24 lines of JavaScript. Inspired by prior art like [Flux](https://facebook.github.io/flux/) and...

Redux is among the most important JavaScript libraries ever created. In this posr, you'll learn how to Implement Redux in 24 lines of JavaScript.

Inspired by prior art like Flux and Elm, Redux put JavaScript functional programming on the map by introducing a scalable architecture of three simple points.

If you're new to Redux, consider reading the official docs first.

Redux Is Mostly Convention

Consider this simple counter application that uses the Redux architecture. If you'd like to jump ahead check out the Github repo for it.

State lives in a single tree

The application's state looks like this.

const initialState = { count: 0 };

Actions declare state changes

By Redux convention, I do not directly modify (mutate) the state.

// DON'T do this in a Redux app
state.count = 1;

Instead I create all the actions the user may leverage in the application.

const actions = {
  increment: { type: 'INCREMENT' },
  decrement: { type: 'DECREMENT' }
};

Reducer interprets action and updates state

The last architectural piece calls for a reducer, a pure function that returns a new copy of your state based on the previous state and action.

  • If increment is fired, increment state.count.
  • If decrement is fired, decrement state.count.
const countReducer = (state = initialState, action) => {
  switch (action.type) {
    case actions.increment.type:
      return {
        count: state.count + 1
      };

    case actions.decrement.type:
      return {
        count: state.count - 1
      };

    default:
      return state;
  }
};

No Redux so far

Did you notice that we haven't touched the Redux library yet? We've just created some objects and a function. This is what I mean by "mostly convention", 90% of Redux doesn't require Redux!

Let's implement Redux

To put this architecture to use, we must plug it into a store. We'll implement just one function–createStore.

It's used like this.

import { createStore } from 'redux'

const store = createStore(countReducer);

store.subscribe(() => {
  console.log(store.getState());
});

store.dispatch(actions.increment);
// logs { count: 1 }

store.dispatch(actions.increment);
// logs { count: 2 }

store.dispatch(actions.decrement);
// logs { count: 1 }

And here's our initial boilerplate. We'll need a list of listeners and the initial state supplied by the reducer.

const createStore = (yourReducer) => {
    const listeners = [];
    let currentState = yourReducer(undefined, {});
}

Whenever someone subscribes to our store, they get added to the listeners array. The is important because every time someone dispatches an action, all the listeners must be notified in a loop.

Calling yourReducer with undefined and an empty object returns the initialState we installed up above. This gives us a proper value to return when we call store.getState(). Speaking of which, let's create that method.

store.getState()

This is a function that returns the latest state from the store. We'll need this to update our UI every time the user clicks a button.

const createStore = (yourReducer) => {
    const listeners = [];
    let currentState = yourReducer(undefined, {});

    return {
        getState: () => currentState
    };
}

store.dispatch(action)

This is a function that takes an action as a parameter. It feeds that action and the currentState to yourReducer to get a new state. Then dispatch notifies everyone subscribed to the store.

const createStore = (yourReducer) => {
  const listeners = [];
  let currentState = yourReducer(undefined, {});

  return {
    getState: () => currentState,
    dispatch: (action) => {
      currentState = reducer(currentState, action);

      listeners.forEach((listener) => {
        listener();
      });
    }
  };
};

store.subscribe(listener)

This is a function that lets you be notified when the store receives an action It's good to use store.getState() in here to get your latest state and update your UI.

const createStore = (yourReducer) => {
  const listeners = [];
  let currentState = yourReducer(undefined, {});

  return {
    getState: () => currentState,
    dispatch: (action) => {
      currentState = reducer(currentState, action);

      listeners.forEach((listener) => {
        listener();
      });
    },
    subscribe: (newListener) => {
      listeners.push(newListener);

      const unsubscribe = () => {
        listeners = listeners.filter((l) => l === newListener);
      };

      return unsubscribe;
    }
  };
};

subscribe returns a function called unsubscribe that you can call when you're no longer interested in listening to the store's updates.

All Together Now

Let's hook this up to our buttons and view the final source code.

// simplified createStore function
const createStore = (reducer) => {
  const listeners = [];
  let currentState = reducer(undefined, {});

  return {
    getState: () => currentState,
    dispatch: (action) => {
      currentState = reducer(currentState, action);

      listeners.forEach((listener) => {
        listener();
      });
    },
    subscribe: (newListener) => {
      listeners.push(newListener);

      const unsubscribe = () => {
        listeners = listeners.filter((l) => l === newListener);
      };

      return unsubscribe;
    }
  };
};

// Redux architecture pieces
const initialState = { count: 0 };

const actions = {
  increment: { type: 'INCREMENT' },
  decrement: { type: 'DECREMENT' }
};

const countReducer = (state = initialState, action) => {
  switch (action.type) {
    case actions.increment.type:
      return {
        count: state.count + 1
      };

    case actions.decrement.type:
      return {
        count: state.count - 1
      };

    default:
      return state;
  }
};

const store = createStore(countReducer);

// DOM elements
const incrementButton = document.querySelector('.increment');
const decrementButton = document.querySelector('.decrement');

// Wire click events to actions
incrementButton.addEventListener('click', () => {
  store.dispatch(actions.increment);
});

decrementButton.addEventListener('click', () => {
  store.dispatch(actions.decrement);
});

// Initialize UI display
const counterDisplay = document.querySelector('h1');
counterDisplay.innerHTML = parseInt(initialState.count);

// Update UI when an action fires
store.subscribe(() => {
  const state = store.getState();

  counterDisplay.innerHTML = parseInt(state.count);
});

And once again here's our final UI.

If you're interested in the HTML/CSS I used, here's the GitHub repo again!

Thanks for reading

Redux Basics

Redux Basics

In this post, the simplest React Redux tutorial I wish I had when I started learning. ... Having seen the basics let's now talk about what problem Redux tries to solve.

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!

You may also like: Angular vs React vs Vue: Which one will be popular in 2020.

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: [...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: [...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. Thank you for reading !

Full-Stack JavaScript Tutorial for Beginners

Full-Stack JavaScript Tutorial for Beginners

Learn full-stack web development using JavaScript (ReactJS, NodeJS, LoopbackJS, Redux and Material-UI)!

Get started as a full-stack JavaScript developer with this 2.5 hour tutorial for beginners!

In this video we will get started with NodeJS and move onto a React development project. This video is a 2.5 free preview pulled from The Complete Full-Stack JavaScript Course on Udemy. If you enjoy this video feel free to checkout the complete course. https://learnstartup.net/p/nMA3zKCMO

Time Stamps

00:41 Intro to the Course

11:59 NodeJS Environment Setup

24:24 Breaking Down a ReactJS Project

37:33 Writing a React Component

46:24 The React Lifecycle

51:19 The React State

1:06:35 Understanding Component Props and Re-rendering

1:24:12 Importing CSS into React Components

1:28:48 Calculator Project Part 1

1:47:30 Calculator Project Part 2

Thanks for reading

If you liked this post, please do share/like it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading

JavaScript Programming Tutorial - Full JavaScript Course for Beginners

New ES2019 Features Every JavaScript Developer Should Know

Best JavaScript Frameworks, Libraries and Tools to Use in 2019

JavaScript Basics Before You Learn React

Build a CMS with Laravel and Vue

Google’s Go Essentials For Node.js / JavaScript Developers