Redux manufacturing: building cohesion

This is the final post, “Building cohesion”, in Redux manufacturing, an introductory series to the JavaScript library Redux. New readers are encouraged to start at the beginning of this series, “Learning by analogy”, here.

In the previous part of this series, we introduced Redux to our application, and began to explore actions, reducers, the store, and containers. To recap:

  • Actions provide an interface into modifying the state of our application.
  • A reducer handles updating state.
  • The store is our interface to actions and reducers: it provides access to the application state, as well as allowing updating the store via dispatching actions.
  • Containers are React components that interface with our Redux store–a component is “wrapped” with a connect function that ties it explicitly to the Redux store.

In the previous post, we implemented a single action in Redux: addWidget. This action was passed as a prop to the App component, and could be called in the interface. When the action was called, a new widget was added via the addWidget function in the reducer. This function returned a new version of application state with an incremented widgets count, but with the necessary raw materials removed from our materials object.

A note at this point: the work in this post is contained in a single commit, located here. A summarized collection of code is available below, with the complete source available at bytesizedxyz/thewidgetcompany on GitLab.

The “thunked” action

The process of creating a widget has two different potential failures:

  • Not enough raw materials to create a widget
  • The widget fails the QA check

The QA check, as you may recall, is a simple random number check–this happens as a function in the reducer. The first error, where not enough materials are present, is more complex than it first appears.

Consider the basic addWidget action that we made in the previous post:

// actions.js

export function addWidget() {
  return {
    date: Date.now(),
    type: ADD_WIDGET
  } 
}

As it stands, this action doesn’t contain any logic. It returns a static object that always contains the ADD_WIDGET type, and a generated date. If we were to check in this action that we have the raw materials for a widget, we would need additional information: specifically, our current application state.

At this point, our action needs to be a function, not just a plain object. To implement this, we’ll use redux-thunk, a simple library written by Redux’s author. redux-thunk allows you to create a thunk, which is an action that allows you to delay its execution. Let’s add redux-thunk to our application, first by adding the necessary middleware to our store:

// index.js
import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
import appState from './reducers';

const store = createStore(
  appState,
  applyMiddleware(thunk)
);

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

With the redux-thunk middleware in our store, we can change our addWidget action to check for the necessary materials–if they exist, proceed to create a widget; if not, call the new presentError action, which will dispatch an error to our store. We’ll rename the addWidget action to attemptWidgetCreation, to indicate that this new action entails a bit of logic about our application:

// constants.js
export const ADD_WIDGET = 'ADD_WIDGET'
export const PRESENT_ERROR = 'PRESENT_ERROR'

export const DOWELS_NEEDED = 1
export const SCREWS_NEEDED = 2
export const WHEELS_NEEDED = 2

// actions.js
import {
  ADD_WIDGET,
  PRESENT_ERROR,
  DOWELS_NEEDED,
  SCREWS_NEEDED,
  WHEELS_NEEDED
} from './constants'

export function attemptWidgetCreation() {
  return (dispatch, getState) => {
    const { materials } = getState()
    const { dowel, screw, wheel } = materials

    if (dowel.count >= DOWELS_NEEDED && screw.count >= SCREWS_NEEDED && wheel.count >= WHEELS_NEEDED) {
      dispatch(addWidget())
    } else {
      dispatch(presentError("Not enough materials to create a widget"))
    }
  }
}

export function addWidget() {
  return {
    date: Date.now(),
    type: ADD_WIDGET
  }
}

export function presentError(message) {
  return {
    type: PRESENT_ERROR,
    message
  }
}

The new function returned in attemptWidgetCreation allows us to defer the execution of the action. We retrieve the state from the getState function, and ensure that the number of dowels, screws, and wheels is enough to create a widget. Once we’ve done this check, we can either dispatch the original addWidget action, or presentError, which accepts a message to be passed into application state.

In our reducer, the code to handle the ADD_WIDGET action remains the same. More code is added to handle PRESENT_ERROR, which will set error in our application state to the provided message:

// reducers.js
const addWidget = (state, date) => {
  const newMaterials = state.materials
  newMaterials.dowel.count -= DOWELS_NEEDED
  newMaterials.screw.count -= SCREWS_NEEDED
  newMaterials.wheel.count -= WHEELS_NEEDED

  let newState = { materials: newMaterials }

  if (qaCheck()) {
    const newWidget = { created: date }
    const newWidgetsInventory = [].concat(state.widgets, newWidget)
    newState.error = null
    newState.widgets = newWidgetsInventory
  } else {
    newState.failed = state.failed += 1
    newState.error = "A widget failed QA!"
  }

  return Object.assign({}, state, newState)
}

const qaCheck = () => {
  const check = Math.floor(Math.random() * 10)
  return check < 7
}

const presentError = (state, message) => {
  return Object.assign({}, state, {
    error: message
  })
}

export default function appState(state = initialState, action) {
  switch (action.type) {
    case ADD_WIDGET:
      return addWidget(state, action.date)
    case PRESENT_ERROR:
      return presentError(state, action.message)
    default:
      return state
  }
}

Update: acemarke on Reddit points out that generating a Date in a reducer makes it impure–this goes against standard Redux practices. The relevant code in both our action and reducer function have been updated, with a new constructed Date instance being generated in the action and passed to the reducer. See the relevant commit here.

This “thunked” action should be used when you need to make a determination around what action should be performed based on state. It can also be used for request-based actions–if you needed to make a request and then update the store based on the response, you could do something like:

export function getThing() {
  return dispatch => {
    fetch(aURL).then(resp => dispatch(updateThing(resp)))
  }
}

Of course, not all actions require being wrapped in a thunk. Our addWidget and presentError actions are immediate–when called, they instantly return an object with a distinguishing action type. In the case of presentError, we see that actions can accept arbitrary arguments, which can be passed along to the store or used for determining logic in the action itself.

All the actions

Packaging orders

Thunked actions are interesting, but most of the actions that take place in our application are straightforward enough that we’ll only create one more instance of this action type. When an order is packaged, the current implementation checks the available number of widgets and compares it to the current order: if there are enough widgets, we can package a new order; if not, an error message should be presented. The action replacement for packageOrder, checkOrderForPackaging, retrieves the state and confirms that enough widgets exist before continuing to package the order:

// constants.js
export const PACKAGE_ORDER = 'PACKAGE_ORDER'

// actions.js
export function checkOrderForPackaging() {
  return (dispatch, getState) => {
    const { orders, widgets } = getState()
    const order = orders[0]
    if (order.widgets <= widgets.length) {
      dispatch(packageOrder(order))
    } else {
      dispatch(presentError("Not enough widgets to fill order!"))
    }
  }
}

export function packageOrder(order) {
  return {
    type: PACKAGE_ORDER,
    order
  }
}

// reducers.js
const packageOrder = (state, order) => {
  const newWidgets = state.widgets

  for (var i=0; i < order.widgets; i++) {
    newWidgets.shift()
  }

  let newOrders = [].concat(state.orders)
  newOrders = newOrders.filter((obj, index) => index != state.orders.indexOf(obj))

  return Object.assign({}, state, {
    error: null,
    orders: newOrders,
    packaged: state.packaged + 1,
    widgets: newWidgets
  })
}

export default function appState(state = initialState, action) {
  switch (action.type) {
        // ...
    case PACKAGE_ORDER:
      return packageOrder(state, action.order)
    default:
      return state
  }
}

(Note that there are two bugs with the primary commit for this post. The fixed implementation is shown above, but two commits are available in GitLab to fix the primary commit. The first fixes a bug with the checkOrderForPackaging implementation, and is available here. The second fixes the reducer behavior of packaging an order, and is available here.)

In implementing this logic in the action and reducer, our Packaging component now simply accepts the checkOrderForPackaging action from the App component–all the determination around when and how to package an order is removed from our view layer:

// App.js
class App extends Component {
  render() {
    const { orders, checkOrderForPackaging, packaged } = this.props
    return (
      <div>
        { /* ... */ }
        <Packaging orders={orders} packaged={packaged} packageOrder={checkOrderForPackaging} />
        { /* ... */ }
      </div>
    )
  }
}

Generating an order

The remainder of our actions are simple, non-thunked implementations. Because of this, we’ll breeze through them since they follow a similar pattern. Generating an order is the first of these:

// constants
export const GENERATE_ORDER = 'GENERATE_ORDER'

// actions.js
export function generateOrder() {
  return {
    date: Date.now(),
    type: GENERATE_ORDER
  }
}

// reducers.js
const generateOrder = (state, date) => {
  const newOrder = {
    created: date,
    widgets: Math.floor(Math.random() * 10) + 1
  }
  const newOrders = [].concat(state.orders, newOrder)
  return Object.assign({}, state, {
    orders: newOrders
  })
}

export default function appState(state = initialState, action) {
  switch (action.type) {
    // ...
    case GENERATE_ORDER:
      return generateOrder(state, action.date)
    // ...
  }
}

None of this code is particularly new–the bulk of the logic, the generateOrder function in reducers.js, is simply ported over from our original implementation in the App component. Instead of referring to this.state, we can look at the current state object passed into the function, which is simply state. Most importantly, we instantiate a new state object, which is compiled from the old state and an object that contains the updated newOrders. Again, this concept of immutable state and not mutating the object that already exists fits neatly into the tenets of designing an application with Redux.

Ordering materials

Ordering materials is a similar flow to generating an order:

// constants
export const ORDER_MATERIALS = 'ORDER_MATERIALS'

// actions.js
export function orderMaterials() {
  return {
    type: ORDER_MATERIALS
  }
}

// reducers.js
const orderMaterials = (state) => {
  const { materials } = state
  const { dowel, screw, wheel } = materials
  return Object.assign({}, state, {
    error: null,
    materials: {
      dowel: { count: dowel.count + 10 },
      screw: { count: screw.count + 10 },
      wheel: { count: wheel.count + 10 }
    }
  })
}

export default function appState(state = initialState, action) {
  switch (action.type) {
    // ...
    case ORDER_MATERIALS:
      return orderMaterials(state)
    // ...
  }
}

Shipping an order

Finally, shipping an order is likely the easiest implementation so far. Note the lack of any variable definitions in the reducer function: it just increments based on what is already in state:

// constants
export const SHIP_ORDER = 'SHIP_ORDER'

// actions.js
export function shipOrder() {
  return {
    type: SHIP_ORDER
  }
}

// reducers.js
const shipOrder = (state) => {
  return Object.assign({}, state, {
    error: null,
    packaged: state.packaged - 1,
    shipped: state.shipped + 1
  })
}

export default function appState(state = initialState, action) {
  switch (action.type) {
    // ...
    case SHIP_ORDER:
      return shipOrder(state)
    // ...
  }
}

(Note that the primary commit for this post is accidentally missing the shipOrder action displayed above. A fix for this is available here.)

View the final version of The Widget Company application at bytesizedxyz/thewidgetcompany on GitLab.

Conclusion

We began this series exploring how the warehouse suffered from a number of confusing design details. Like many software applications, it was unclear how data moved between components, and the responsibility of a component determining how and when it should perform an action was quite vague.

Implementing the original App component with a top-level state was a bit of a trick: in doing so, we already gave ourselves a head-start on implementing the same behavior in Redux. With the addition of Redux into our project, our data flow became unidirectional. Data flows from the store, down through the container, into the components. Actions flow much in the same way, and when one is called, the new version of state is passed down just as it had already done once before.

Understanding unidirectional data flow isn’t just key to understanding React; it’s crucial in designing high-functioning React applications. Given a new feature or piece of functionality, it’s clear where it should go. Given a request to modify existing behavior, we can always be sure where that logic is defined. This application has gone from relative instability to a straightforward, well-separated collection of React components, and vanilla JavaScript code.

Where to proceed from here? Given the availability of redux-thunk in our project, we’re in a position to implement any number of complex behaviors in our application. If we wanted to move our order generation to a different application altogether, it’s a simple thunked request away. Making the QA department more complex by introducing an explicit check based on a network request, or an attribute of the widget, is feasible–we know where the current code lives, and how and where our updated version could live.

At the beginning of this series, I explained why analogy is an effective tool for learning:

Learning by analogy has proven to be an incredibly effective method I’ve used to learn a variety of new things. The reason for this is simple: it is tangibly easier to connect pieces you already have over discovering and remembering entirely new pieces. After all, when building anything, it’s necessary to first take stock of what you have, before determining what new things that you need.

It’s difficult to explain why unidirectional data flow matters. It’s hard to grasp at why separating actions and reducers from your components is useful. By implementing a real application, and by allowing each component in the application to model a (somewhat real) concern in our warehouse analogy, it became easier to see the project evolve over time. Our initial implementation wasn’t bad, but the advantages of the Redux approach should be clear. Overall, we’ve built an application that I would be happy to continue to work on in the future, and one that new developers would be quite comfortable getting up and running with.

Found this series useful? Check out some of the other posts on our blog, like the free React.js series “A year with React.js”.

Bytesized offers technical training for companies. If you or your company want to take a deeper dive into React.js or Redux, including hands-on workshops, lectures, and take-home exercises, contact us to schedule training. We’re excited to work with you.

Redux manufacturing: managing state and inventory

This is part three, “Managing state and inventory”, of Redux manufacturing, an introductory series to the JavaScript library Redux. New readers are encouraged to start at the beginning of this series, “Learning by analogy”, here.

In the previous post in this series, we built out the initial components that modeled creating widgets in our warehouse. We defined a top-level collection of objects, known as state, that collected raw materials, produced widgets, and any error messages or “bad” widgets (determined by the warehouse’s QA department). This state was stored in the App component: in doing so, we were able to pass various parts of state (materials, widgets) into the components as props that interfaced with those objects.

We also defined an addWidget action, which was passed into the Manufacturing component. This action also was passed as a prop to the component, and in doing so, the actual “logic” around what creating a component entails is not strictly tied to the view layer.

In this post, we’ll finish implementing the basic pieces of our warehouse–we’ll create orders, which contain a set number of required widgets, and allow packaging and shipping these orders. Once we’ve built this functionality, we’ll introduce Redux, and begin to understand how it manages state, and modifying data via actions.

Generating orders

From the perspective of a customer, the order is essentially the only view into our warehouse. This is an oversimplification, as orders themselves are very interesting. An order is self-contained when created: it contains a timestamp, indicating when it was created (this is to have a concept of “uniqueness” in our HTML code–see React’s keys documentation for more info), as well as the number of widgets required to fill the order.

Filling the order is more interesting. Not only is the order itself “destroyed” in the process of being filled, as it changes into a packaged order; this process of filling the order also removes widgets from the widget part of our state. Because of this, the flow of an order throughout the app touches almost every part of state.

The Orders component itself receives two props: an action, generateOrder, which creates a new order and appends it to the orders array in state, and the orders array itself, to display each order and the number of required widgets to fill it:

// App.js

import Orders from './Orders'

class App extends Component {
  constructor() {
    super()

    this.state = {
      // ...
      orders: []
      // ...
    }

    this.generateOrder = this.generateOrder.bind(this)
  }

  generateOrder() {
    const newOrder = {
      created: Date.now,
      widgets: Math.floor(Math.random() * 10) + 1
    }
    const newOrders = [].concat(this.state.orders, newOrder)
    const newState = Object.assign({}, this.state, {
      orders: newOrders
    })
    this.setState(newState)
  }

  render() {
    const { error, failed, materials, orders, widgets } = this.state
    return (
      <div>
        { /* ... */ }
        <Orders 
          generateOrder={this.generateOrder} 
          orders={orders} />
        { /* ... */ }
      </div>
    );
  }
}

// Orders.js

class Orders extends Component {
  render() {
    const { generateOrder, orders } = this.props
    return (
      <div>
        <h2>Orders</h2>
        <h3>{orders.length} orders</h3>
        <ul>
          {orders.map(order => <li key={order.created}>Order for {order.widgets} widgets</li>)}
        </ul>
        <button onClick={generateOrder}>Generate order</button>
      </div>
    )
  }
}

(commit 0c6741)

Packaging orders

The Packaging component receives three props, but this doesn’t mean that it’s especially complicated. The packageOrder, much like our previous actions, removes the most recent order from the orders array and, given enough available widgets, packages them to increment the packaged count. It also receives packaged and orders, in order to display a button for packaging an order. If no orders are available, the button is hidden from the interface.

At this point, you may find that it becomes difficult in the UI to actually build and package widgets, because the number of raw materials remains static. The Management component provides a simple button to increase the number of available raw materials. Of course, this component could be expanded with a number of other functions, but I found that this was a necessary addition to actually test the end-to-end flow of widget delivery in the application. Code for the addition of both components follows:


// App.js import Management from './Management' import Packaging from './Packaging' class App extends Component { constructor() { super() this.state = { // ... packaged: 0 // ... } this.orderMaterials = this.orderMaterials.bind(this) this.packageOrder = this.packageOrder.bind(this) } // ... orderMaterials() { const { materials } = this.state const { dowel, screw, wheel } = materials this.setState(Object.assign({}, this.state, { materials: { dowel: { count: dowel.count + 10 }, screw: { count: screw.count + 10 }, wheel: { count: wheel.count + 10 } } })) } packageOrder() { const { orders, packaged, widgets } = this.state const newOrders = [].concat(orders) const order = newOrders.pop() if (order.widgets <= widgets.length) { this.setState({ orders: newOrders, packaged: packaged + 1, widgets: widgets - order.widgets }) } else { this.presentError("Not enough widgets to fill order!") } } // ... render() { const { packaged } = this.state return ( <div> { /* ... */ } <Packaging orders={orders} packaged={packaged} packageOrder={this.packageOrder} /> <Management orderMaterials={this.orderMaterials} /> </div> ); } } // Management.js class Management extends Component { render() { return ( <div> <h2>Management</h2> <button onClick={this.props.orderMaterials}>Order raw materials</button> </div> ); } } // Packaging.js class Packaging extends Component { render() { const { orders, packageOrder, packaged } = this.props return ( <div> <h2>Packaging</h2> <h4>{packaged} orders packaged</h4> {orders.length ? <button onClick={packageOrder}>Package order</button> : <h4>No orders to package</h4>} </div> ); } }

(commit 0c6741)

Shipping orders

The Shipping component is incredibly simple, and somewhat of a let-down for our finale. The shipOrder prop delegates shipping a package to our shipOrder function, which simply increments the number of shipped orders in state, while decrementing the number of packaged orders. Again, we pass in packaged to hide the “Ship order” button if there are no packages to ship, and shipped to count how many packages have been shipped.

// App.js

import Shipping from './Shipping'

class App extends Component {
  constructor() {
    super()

    this.state = {
      // ...
      shipped: 0,
      // ...
    }

    // ...
    this.shipOrder = this.shipOrder.bind(this)
  }

  // ...

  shipOrder() {
    const { packaged, shipped } = this.state
    this.setState({
      error: null,
      packaged: packaged - 1,
      shipped: shipped + 1
    })
  }

  render() {
    const { shipped } = this.state
    return (
      <div>
        { /* ... */ }
        <Shipping packaged={packaged} shipped={shipped} shipOrder={this.shipOrder} />
        { /* ... */ }
      </div>
    );
  }
}

// Shipping.js

class Shipping extends Component {
  render() {
    const { packaged, shipped, shipOrder } = this.props
    return (
      <div>
        <h2>Shipping</h2>
        <h4>{shipped} orders shipped</h4>
        {packaged ? <button onClick={shipOrder}>Ship order</button> : <h4>No orders to ship</h4>}
      </div>
    );
  }
}

(commit cf6299)

Introducing Redux: Data flow

Throughout this series, I’ve been intentional in introducing our data and actions in a single place as much as possible: the App component. Because of this, we can easily intuit how the data flows, and how actions interface with the application state. Parts of our application state, like widgets or error, are pushed into components, but they’re never modified in the component themselves. Instead, we pass a function, like addWidget, as a prop to be called by the component. In doing so, we update state in App using .setState, and pass the new version of state into the respective components.

This may have seemed verbose, but it was for a reason–this is almost identical to how we will handle data with Redux.

A great deal of confusion can come out of trying to introduce Redux to an existing project. The reason for this isn’t because writing Redux code is particularly hard; it’s because we often don’t understand how data flows in our application. Redux makes this a non-issue, because you have no choice.

Data is always unidirectional in Redux. The data arrives at the top level component, and is passed down to various components as needed, often as subsets of the original state–for instance, passing only error to the Error component. These components, where state is essentially “injected”, are called containers.

There are four different things we must know to implement Redux in our application: actions, reducers, the store, and containers. We’ll cover each of them in brief here, but in the next post, we’ll rewrite our application to use them directly, and thus have a complete React and Redux application.

Actions

Actions provide an interface into modifying the state of our application. The functions we’ve written and passed as props into various components are similar in concept to Redux actions–they act as signals to update state. The difference is that a Redux action does not actually do the state updating itself. Instead, it passes along an identifier to a reducer to let it know that state needs to be updated. Below is an example of what an addWidget action might look like in Redux:

// actions.js

export function addWidget() {
  return {
    date: Date.now(),
    type: ADD_WIDGET
  } 
}

Reducers

A reducer handles updating state. A reduce operation takes an input and a function, and applies them to produce a single output. In the same way, a Redux reducer receives state as input, and updates it based on the requested action–for instance, ADD_WIDGET:

// reducers.js

import {
  ADD_WIDGET,
  DOWELS_NEEDED,
  SCREWS_NEEDED,
  WHEELS_NEEDED
} from './constants'

const initialState = {
  error: null,
  failed: 0,
  orders: [],
  materials: {
    dowel: { count: 2 },
    screw: { count: 8 },
    wheel: { count: 3 }
  },
  packaged: 0,
  shipped: 0,
  widgets: []
}

const addWidget = (state, date) => {
  const newMaterials = state.materials
  newMaterials.dowel.count -= DOWELS_NEEDED
  newMaterials.screw.count -= SCREWS_NEEDED
  newMaterials.wheel.count -= WHEELS_NEEDED

  let newState = { materials: newMaterials }

  if (qaCheck()) {
    const newWidget = { created: date }
    const newWidgetsInventory = [].concat(state.widgets, newWidget)
    newState.error = null
    newState.widgets = newWidgetsInventory
  } else {
    newState.failed = state.failed += 1
    newState.error = "A widget failed QA!"
  }

  return Object.assign({}, state, newState)
}

const qaCheck = () => {
  const check = Math.floor(Math.random() * 10)
  return check < 7
}

export default function appState(state = initialState, action) {
  switch (action.type) {
    case ADD_WIDGET:
      return addWidget(state, action.date)
    // ...
    default:
      return state
  }
}

Update: acemarke on Reddit points out that generating a Date in a reducer makes it impure–this goes against standard Redux practices. The relevant code in both our action and reducer function have been updated, with a new constructed Date instance being generated in the action and passed to the reducer. See the relevant commit here.

It’s important to note the introduction of constantsADD_WIDGET, for instance, is used in both our action example and in our reducer. By defining a single source of truth for our action names, we can ensure that the actions and reducers properly handle the same actions. Under the hood, constants are simply strings:

export const ADD_WIDGET = 'ADD_WIDGET'

The usage of constants also makes debugging straightforward: with a tool like redux-logger, you can follow your application as it moves through actions by looking for ADD_WIDGET. Because it’s unlikely this string will be used in any place besides your actions and reducers, it brings some extra clarity to your application.

The initialState variable, identical to our initial state in the App component, is provided as the default argument to appState. The appState function receives a state object and an action, and depending on what the action is, returns a variant of the state argument, or leaves it unchanged.

The addWidget function pulls out the relevant logic into a separate method, keeping the appState function relatively clean. It’s important to know that your state object should be treated as immutable: instead of modifying the state argument passed in to addWidget, you should create a new object and assign the relevant parts of the existing state, as well as overwrite the new/updated state from newState. Redux’s documentation contains an incredibly detailed section on immutability, but it’s also succinctly described in one of Redux’s core tenets: never mutate state.

By providing a pure reducer function where state comes in and new state comes out, we’re architecting a data flow in which it’s always clear how and when data changes. Changes to state only happen in the reducer; changes to state are always propagated to components. It’s a great mental model that makes debugging and reasoning about your code easier.

Containers and stores

Actions and reducers are basically a collection of simple JavaScript objects and functions. Without connecting them to our application, they’re fairly useless. This is where the store and containers come in. A Redux store is our interface to actions and reducers: it provides access to the application state, as well as allowing updating the store via dispatching actions. Containers are React components that interface with our Redux store–a component is “wrapped” with a connect function that ties it explicitly to the Redux store.

While the implementation of “connecting” a container to the Redux store isn’t particularly difficult, it’s recommended that you make use of react-redux, which exposes a simple API for creating containers. To begin, we’ll wrap our App component in a higher-order component, or HOC. An HOC receives a component as input, and returns a new component as output. react-redux provides the Provider component, which accepts a store prop and makes it available to its wrapped component; in our case, App:

// index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import appState from './reducers';
import App from './App';
import './index.css';

const store = createStore(appState);

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

Our App component now has access to our Redux store. We can use the connect function from react-redux to gain access to our application state and actions:

// App.js
// ...
import { connect } from 'react-redux'
import { addWidget } from './actions'

class App extends Component {
  render() {
    const {
      addWidget,
      widgets
    } = this.props

    return (
      // ...
    );
  }
}

const mapStateToProps = (state) => state
const mapActionCreators = { addWidget }

export default connect(
  mapStateToProps,
  mapActionCreators
)(App);

(commit b13516)

(Looking for the rest of the action and reducer code? Check out the next post in this series, where we fully transition our application state and actions into Redux.)

The connect function requires two arguments–a mapStateToProps function, which takes application state as an input and prepares it to be injected as props to our component, and a mapActionCreators object, which prepares our actions to be callable as props from our component.

In the render function, the widgets portion of state is now available inside of this.props, whereas previously we retrieved it from this.state. In the same way, our addWidget action, previously defined on the component itself, is now just a prop. Either can be passed to child components as simple props, and calling addWidget would fire the action on our Redux store, and potentially update widgets via the reducer.

Conclusion

In implementing Redux in our application, we’ve removed almost all the logic of operating the warehouse from the components itself. This makes sense if you recall React’s design as “the view layer”: it shouldn’t really be concerned with how the warehouse operates. It should only focus on providing the interface to operating the warehouse.

In the final part of this series, we’ll fully transition our warehouse application into a Redux and React application. We’ll explore more complex reducer logic, and migrate our application actions using both basic actions, and “thunked” actions.

Bytesized offers technical training for companies. If you or your company want to take a deeper dive into React.js or Redux, including hands-on workshops, lectures, and take-home exercises, contact us to schedule training. We’re excited to work with you.

Read the next post in this series, “Building cohesion”.

Redux manufacturing: introducing the warehouse floor

This is part two, “Introducing the warehouse floor”, of Redux manufacturing, an introductory series to the JavaScript library Redux. Readers are encouraged to read the first part of this series, “Learning by analogy”, here.

In the first part of this series, we explored why we’ll use analogy to explore Redux. As I mentioned in the previous post, Redux can be really difficult to pick up:

I’ve worked with Redux since 2015, shortly after the first version of it was released. In my experience since then, working on applications of wildly different scale, and with a variety of developers at different experience levels, a single conclusion has come front and center: learning Redux is hard.

I haven’t yet encountered a developer on any software team I’ve worked on that has intuitively picked up Redux. There’s a learning curve––the primary reason, in my opinion, is because it is all new. The concept of data flow is usually handled for you (such as in Ember.js), and managing it yourself and defining such strict logic around it can be really uncomfortable to pick up.

When you do learn Redux, it seems so simple in retrospect: it becomes difficult to remember just why it was so complicated to begin with. This is because while the concepts themselves are straightforward, they’re radically different in many ways from what “conventional” web development has looked like in the past.

Our solution is the analogy: taking what we already know, and remixing it to understand an entirely new concept. This series uses the warehouse floor, where workers come together to build a (hopefully) functioning supply chain to ship products to customers. The warehouse floor is a familiar scenario in project management and “flow” circles, as it has been used in business parables like “The Goal” to explain effective management and process.

The Widget Company

Today, I’ll introduce the players of the warehouse floor. We’ll understand how they work, and more importantly, where they conflict in problematic ways. We’ll also begin to model a React application to show how the different pieces of our warehouse will be represented with data.

Our fictitious company, The Widget Company (“TWC”), which builds trendy “Widgets”, has a few departments–the Orders department collects widget orders from customers, and delivers them to the warehouse.

The warehouse has a number of departments:
Raw materials is an inventory of the components for creating widgets.
Manufacturing takes raw materials and combines them into widgets.
QA, or Quality Assurance, looks at the completed widgets and gives them the “OK”–they’re acceptable to send to customers.
Inventory holds a large number of widgets until they are used as part of an order, as well as packaged items that are ready for shipping.
Packaging takes an order, and prepares widgets by wrapping and boxing them for a customer, before putting them back into inventory.
Shipping takes a packaged order from inventory, and delivers the widgets to a customer.

If it seems like there’s a natural flow to the warehouse, you’re correct. The basic progression of a widget through the warehouse looks like this:

Sometimes things get complicated

While the flow of a widget itself may seem complex, the ordering and delivery process is anything but. As it stands, TWC suffers from a variety of problems:
– Many orders come in when there are no widgets in the Inventory, or when no orders arrive, there are too many widgets–this understocking or overstocking is expensive and time-consuming.
– The Ordering department contacts Packaging directly to begin preparing an order for shipment–if a massive number of new orders come in, Manufacturing is left unaware, leading to the above understock/overstock situation.
– Given an understocking situation, orders are often “rushed”, which causes widgets to skip QA. This leads to customer dissatisfaction and losing money on refunds/exchanges.

Additionally, some departments have too much knowledge of other departments–Inventory holds both assembled and packaged items, causing confusion when Shipping prepares an order. Both QA and Packaging are delivering items to Inventory, and it is hard for Management, which has so far been unrepresented in our diagrams, to understand where and how work is proceeding.

TWC is facing a crisis: due to warehouse inefficiency and cost, they are reaching dangerous financials lows. A once booming widget business is on the brink of ruin. An accurate representation of TWC might be something like this:

Where’s the code?

It’s a dire picture, but what does it have to do with Redux? What does it have to do with React?

We’re going to model TWC with React and Redux. In doing so, we’re going to explore the issues that plague their production flow, and hopefully fix them, using Redux’s inherently smart design.

This application will be written using syntax blocks directly in the post, but as a full application, it will also be available via GitLab. I’ll link to specific Git commits at each code point, but the complete repository is also available at bytesizedxyz/thewidgetcompany.

Note that in this tutorial, we’ll condense our code samples, preferring to just show the most important part of each commit–at any point in the tutorial, you can use Git to get the exact version of the application at that point in time: git checkout.

Using create-react-app, we’ll build a React application that you can run locally, to “create” and “ship” widgets. The README.md file in the above GitLab repo provides instructions on how to do this.

Welcome to The Widget Company

We’ll begin by initializing our application––as I mentioned, create-react-app is a good resource for this. Our first pass at a foundation to build on is simply an App component, which returns the name of our company:

import React, { Component } from 'react';
import "./index.css"
import "./normalize.css"

const Header = () => <h1>The Widget Company</h1>

class App extends Component {
  render() {
    return (
      <div>
        <Header />
      </div>
    );
  }
}

(commit 16c556)

With our application representing the warehouse, we can began to instantiate each part of the warehouse floor as a component. To begin, we’ll need to model our raw material store, which will display the current parts that we have to build from. A “widget” is made of five pieces: two wheels, a dowel, and two screws to attach them all together. Our Materials component will track how many of each piece we have, as well as display them in a basic HTML list:

class Materials extends Component {
  render() {
    const materials = [
      { name: 'Dowel', count: 2 },
      { name: 'Screw', count: 8 },
      { name: 'Wheel', count: 3 }
    ]

    return (
      <div>
        <h2>Raw Materials</h2>
        <ul>
          {materials.map(material => <li>{material.name}: {material.count}</li>)}
        </ul>
      </div>
    );
  }
}

(commit 9edf27)

Thinking ahead, we’ll soon need to pull materials from our raw material store, in order to actually construct widgets. Should we make the materials in our component accessible to the rest of the app?

In short, no. This pattern of sharing state from one component with another, often implemented with React’s “context” feature, is considered a strong antipattern in React: it’s unreliable and is, at this point, strongly discouraged by the React team:

The vast majority of applications do not need to use context.

If you want your application to be stable, don’t use context. It is an experimental API and it is likely to break in future releases of React.

If you aren’t familiar with state management libraries like Redux or MobX, don’t use context. For many practical applications, these libraries and their React bindings are a good choice for managing state that is relevant to many components. It is far more likely that Redux is the right solution to your problem than that context is the right solution.

Instead, we’ll take our materials and bring them into the App component, passing them into Materials and, in the future, whatever other components need access to it. This approach is a common pattern, and foreshadows how we’ll think about our application’s state when we introduce Redux:

// App.js
import Materials from './Materials'

class App extends Component {
  constructor() {
    super()
    this.state = {
      materials: [
        { name: 'Dowel', count: 2 },
        { name: 'Screw', count: 8 },
        { name: 'Wheel', count: 3 }
      ]
    }
  }
  render() {
    const { materials } = this.state
    return (
      <div>
        <Header />
        <Materials materials={materials} />
      </div>
    );
  }
}

// Materials.js

class Materials extends Component {
  render() {
    const { materials } = this.props
    return (
      <div>
        <h2>Raw Materials</h2>
        <ul>
          {materials.map(material => <li>{material.name}: {material.count}</li>)}
        </ul>
      </div>
    );
  }
}

(commit 4c58ad)

With our raw materials available at the App level, we can begin to manufacture widgets. Our Manufacturing component will accept a collection of materials and given that enough of each piece is available, will create a widget.

A few concepts are introduced here. The first is that of “actions”. A HTML button needs to be connected to some action–in React, we often do this by setting the onClick handler for a button. In our Manufacturing component, a manufactureWidget function will check the provided materials: if there’s enough pieces to make a widget, the component calls this.props.addWidget, which creates a widget and adds it to our application state. If there isn’t enough pieces to create a widget, the component calls this.props.presentError with an error message, which is set in state as error and presents as banner text in our application:

// App.js

import Manufacturing from './Manufacturing'

class App extends Component {
  constructor() {
    super()

    this.state = {
      error: null,
      materials: {
        // repeated...
      },
      widgets: []
    }

    this.addWidget = this.addWidget.bind(this)
    this.presentError = this.presentError.bind(this)
  }

  addWidget() {
    const newState = {}
    const newMaterials = this.state.materials
    newMaterials.dowel.count -= 1
    newMaterials.screw.count -= 2
    newMaterials.wheel.count -= 2

    const newWidget = { created: Date.now() }
    const newWidgetsInventory = [].concat(this.state.widgets, newWidget)

    this.setState(Object.assign({}, this.state, {
      materials: newMaterials,
      widgets: newWidgetsInventory
    }))
  }

  presentError(message) {
    this.setState(Object.assign({}, this.state, { error: message }))
  }

  render() {
    const { error, materials, widgets } = this.state
    return (
      <div>
        <Header />
        {error ? <Error message={error} /> : null}
        <Materials materials={materials} />
        <Manufacturing
          addWidget={this.addWidget}
          materials={materials}
          presentError={this.presentError}
        />
      </div>
    );
  }
}

// Manufacturing.js

class Manufacturing extends Component {
  constructor() {
    super()
    this.manufactureWidget = this.manufactureWidget.bind(this)
  }

  manufactureWidget() {
    const { addWidget, materials, presentError } = this.props
    if (materials.dowel.count >= 1 && materials.screw.count >= 2 && materials.wheel.count >= 2) {
      addWidget()
    } else {
      presentError("Not enough materials to create a widget")
    }
  }

  render() {
    return (
      <div>
        <h2>Manufacturing</h2>
        <button 
          onClick={this.manufactureWidget}>
          Manufacture widget
        </button>
      </div>
    );
  }
}

(commit 47b6c1)

There’s a few design choices here that are intentional, but might be missed if you’re a newer React developer. The first is understanding when and where actions occur. The Manufacturing component, responsible for creating widgets, checks the provided materials and calls the action provided to it for creating actions at the top level. The addWidget action in the App component is responsible for actually modifying the state to add a new widget; in doing so, it subtracts pieces from our raw materials and appends a new widget to our application state.

Our action functions live in the App component to give Manufacturing as little information about the actual process of modifying application state as possible. Arguably, even the knowledge about what is needed to create a widget–two wheels, two screws, and a dowel–is too much for the Manufacturing component to know, but in this case, it gives the component something to do. We could, as an exercise, move manufactureWidget into the App component, and pass manufactureWidget as a prop into Manufacturing, instead of appWidget and presentError. For now, this solution works, and also is a precursor to how we’ll think about Redux’s actions, which live outside of React components, and are passed as props into our application.

We’re creating widgets, but we have no way of tracking how many widgets we’ve produced. Of course, we need to define an Inventory component. Unlike our last complicated set of changes, this component accepts widgets from application state, and displays the current number of widgets in inventory:

// App.js

import Inventory from './Inventory'

class App extends Component {
  render() {
    const { error, materials, widgets } = this.state
    return (
      <div>
        // ...
        <Inventory widgets={widgets} />
      </div>
    );
  }
}

// Inventory.js

class Inventory extends Component {
  render() {
    const { widgets } = this.props
    return (
      <div>
        <h2>Inventory</h2>
        <h4>
          {widgets.length} widgets in inventory
        </h4>
      </div>
    );
  }
}

(commit eaa849)

If you remember our initial diagram of the warehouse, you might remember that there is a QA (Quality Assurance) component missing here. To model a QA department, we’ll define an error rate–we’ll say 30%, in our example–and randomize a “check” that must pass for widgets to be created. This function, called qaCheck, lives in the App component, so that it can be called during the addWidget process. When it returns false, and a QA check has failed, we increment failed in our application state, which is counted by the QA component:

// App.js

import QA from './QA'

class App extends Component {
  qaCheck() {
    const check = Math.floor(Math.random() * 10) + 1
    return check < 7
  }

  addWidget() {
    const newMaterials = this.state.materials
    newMaterials.dowel.count -= 1
    newMaterials.screw.count -= 2
    newMaterials.wheel.count -= 2

    let newState = { materials: newMaterials }

    if (this.qaCheck()) {
      const newWidget = { created: Date.now() }
      const newWidgetsInventory = [].concat(this.state.widgets, newWidget)

      newState.widgets = newWidgetsInventory
    } else {
      newState.failed = this.state.failed += 1
      newState.error = "A widget failed QA!"
    }

    this.setState(Object.assign({}, this.state, newState))
  }

  render() {
    const { error, failed, materials, widgets } = this.state
    return (
      <div>
        // ...
        <QA failed={failed} />
        // ...
      </div>
    );
  }
}

// QA.js

class QA extends Component {
  render() {
    const { failed } = this.props
    return (
      <div>
        <h2>QA</h2>
        <h4>{failed} widgets failed QA</h4>
      </div>
    );
  }
}

Note that in the below commit, there’s a small error: the check variable in the qaCheck function, in the below commit, produces numbers between 0 and 9. Checking for greater than 7 when numbers are between 0 and 9 is a 20% error rate, not a 30%, as specified in the requirements. This has been fixed with the addition of + 1 to the end of our check variable, seen in the above code snippet.

(commit 40aaf8)

Our QA department now intercepts the creation of widgets by Manufacturing, and in doing so, provides an opportunity to see how we could deal with “invalid” data in a more complex React application. Given something like these failed pieces of data, we may want to institute some sort of edit-and-retry functionality in a more complex application. By separating failed data into its own part of state, we can easily pass it into an Edit component to allow repair of the data.

At this point, we’ve modeled the first half of our warehouse. We can create widgets and store them in our inventory. In the next part of this series, we’ll look at the ordering process. We’ll allow generation of orders, as well as packaging and shipping orders. We’ll also begin to more carefully examine how we’ve designed our application state, and in doing so, introduce the beginning concepts of Redux to our application.

Bytesized offers technical training for companies. If you or your company want to take a deeper dive into React.js or Redux, including hands-on workshops, lectures, and take-home exercises, contact us to schedule training. We’re excited to work with you.

Read the next post in this series, “Managing state and inventory”.

Redux manufacturing: learning by analogy

This is part one, “Learning by analogy”, of Redux manufacturing, an introductory series to the JavaScript library Redux.

Learning by analogy

I spend a lot of time thinking about how best to learn. There are plenty of different ways to learn more effectively–for instance, how you spend your time or your attention. Taking the law of vital few into account, there are many situations where identifying the invaluable 20% of information can save you 80% of the time–knowing what to study. How you study can be important, too: a tool like Anki can help you reflect and review on material in a way that helps promote retention and understanding.

Learning by analogy has proven to be an incredibly effective method I’ve used to learn a variety of new things. The reason for this is simple: it is tangibly easier to connect pieces you already have over discovering and remembering entirely new pieces. After all, when building anything, it’s necessary to first take stock of what you have, before determining what new things that you need.

I try and tie all kinds of things together via analogies. Sometimes, it doesn’t really work, and the results sound like a bad startup pitch: “React.js is like a toaster” (but props to you if you find any coherent comparisons between the two). When they do work, you can almost feel your brain connecting the dots in a satisfying and memorable way. Here’s one of my favorite analogies, one that I’ve been come back to regularly over the years.

Disclaimer: the next part is a bit of a tangent for those of us interested in the art of learning. Just here for the React and Redux goodness? Skip ahead by clicking here.

Amplitude and frequency

As I began to learn music production, it took me a short amount of time to understand what amplitude and frequency were, but why they were important eluded me for a long time. I had Ableton Live, a wonderfully interactive music production application, so the tools for discovery were there, but I just wasn’t getting it.

For instance, I would look at a kick drum inside of an equalizer. I know now that an equalizer allows you to modify the frequency of a sound, but when I first saw this in the equalizer interface, I had no idea what was happening.

Amplitude and frequency work together to produce every sound that we hear (and even the ones that we don’t hear). A “sound” has an amplitude, better thought of volume, and a frequency, which we often refer to as pitch. A dog whistle, for instance, is a high frequency, low amplitude sound. The sound of a car engine and a drummer’s kick drum at a stadium rock concert are both low frequency, but their amplitudes are radically different: more interestingly, they could both be considered high amplitude, but only in relationship to what is currently around them.

So amplitude and frequency map to volume and pitch, more or less. Why do they matter?

In making music, musicians are able to write with almost no constraints. Any frequency within hearing range is fair game; given an agreeable audience, most amplitudes are OK too. But there are some constraints that musicians are intuitively aware of: sound itself––more specifically, how we hear it. Imagine two different sounds of similar frequencies played at the same time. Which sound would you hear? You might guess both, and that’s true to an extent. Your ear will, however, focus on whichever sound is louder: the sound with higher amplitude.

Music production, then, is a battle of trying to fit your concepts into the small space we’re given with our hearing range. Coming back to our equalizer, we can apply our new knowledge of frequency and amplitude as the X and Y axes of the equalizer graph. The X axis indicates frequency, and the Y axis indicates amplitude. A kick drum, like all sounds, includes frequencies across the entire spectrum––it’s not surprising, however, to see that the loudest part of the kick drum is in the low frequencies.

Frequency and amplitude as a graph; a decent analogy, but we can do better.

Music production is like filling a storage unit with boxes. Boxes can be different sizes, from small, medium, and large. The size of the box corresponds to amplitude: a loud sound is a large box, and a quiet sound is a small box. To produce a song, you must find a way to fit all your boxes (drums, bass, guitar, vocals) into the storage unit. A really large guitar box might make it impossible for the bass and vocals to also fit in the unit; to fix this, you might need to get a smaller guitar box, or forego the box entirely.

It turns out that with this analogy in mind, equalizers make complete sense: they allow you to make certain boxes smaller. In the below example, we use a tool called a high-pass filter inside of Ableton’s equalizer, to remove all the low frequencies from a sound.

It’s safe to say that most people are familiar with boxes, and with the concept of physical space. Analogies are incredible powerful–we can “remix” that fairly basic knowledge as a way to learn frequencies, amplitude, and equalizers. In fact, I’ve used the same analogy before to explain other music production concepts: compressors and limiters can fairly easily be explained using similar concepts. A good analogy can outlast its initial comparison and become a wonderful tool to deeply understand another topic.

The warehouse floor

With this in mind, we now turn our attention to Redux, the popular open-source JavaScript library, often used with React.js. For better or worse, Redux is almost always expected to be introduced as React applications grow in complexity, especially as it relates to application state.

I’ve worked with Redux since 2015, shortly after the first version of it was released. In my experience since then, working on applications of wildly different scale, and with a variety of developers at different experience levels, a single conclusion has come front and center: learning Redux is hard.

I haven’t yet encountered a developer on any software team I’ve worked on that has intuitively picked up Redux. There’s a learning curve––the primary reason, in my opinion, is because it is all new. The concept of data flow is usually handled for you (such as in Ember.js), and managing it yourself and defining such strict logic around it can be really uncomfortable to pick up.

When you do learn Redux, it seems so simple in retrospect: it becomes difficult to remember just why it was so complicated to begin with. This is because while the concepts themselves are straightforward, they’re radically different in many ways from what “conventional” web development has looked like in the past.

Luckily, we know what to do with things that are all new or different, right? You guessed it–analogies. In this series, we’re going to introduce Redux using the analogy of an active warehouse floor, where manufacturing, supplying, and management are all happening at a rapid pace. This analogy is a nod to the Lean manufacturing revolution of the 80s and 90s–manufacturing systems provide a wonderful lab to test new processes and systems. Our heroes of the manufacturing floor are saddled with too much work, not enough organization, and that sense of impending doom that some of us are too familiar with in stressful work situations.

In the next part of this series, I’ll introduce the players of the warehouse floor. We’ll understand how they work, and more importantly, where they conflict in problematic ways. We’ll also begin to model a React application to show how the different pieces of our warehouse will be represented with data.

Bytesized offers technical training for companies. Contact us to schedule Redux training for your team today.

Read the next post in this series, “Introducing the warehouse floor”.