Porting a React and Redux application to TypeScript

I’m increasingly becoming interested in TypeScript, Microsoft’s JavaScript superset. It provides a strongly-typed variant of JS, one that ultimately compiles down to plain JavaScript via a compiler tool like Webpack.

While TypeScript’s official website does have pretty good documentation, there is no real substitute for writing actual code. As such, this is my experience porting a React and Redux application over to TypeScript.

The application I moved is thewidgetcompany, a sample application written as part of Bytesized’s free Redux course, Redux Manufacturing. It’s a fairly simple collection of React components and a few Redux actions: everything is contained locally and there are no external API dependencies. All in all, it’s a good example application to use as a guinea pig for our TypeScript experiments.

It may be useful to briefly summarize thewidgetcompany as an application, before we dive into how to port it into TypeScript. The application contains a number of components, each interfacing with different parts of the application state to create fictional widgets, which are ultimately packaged and shipped. The “flow” of the application can be simplified as: Raw Materials -> Create Widgets -> Package Widgets -> Ship Widgets. A number of the components simply provide a buttons like “Package Order” or “Ship Order”, and send a message to application state to modify the number of materials, packaged orders, etc. The actual logic in the application remains the same throughout our TypeScript migration–instead, we focus on defining types and interfaces around those existing objects and actions, to ensure that the functionality is bug-free and consistent.

Extensions

TypeScript files are indicated by the extension .ts – if the file is a JSX/React file, you can also use .tsx. Because this is a React application, we’ll move our code over to .tsx.

We could manually rename all of our JS files in our src directory, but the command-line always has a solution for these kind of problems. In Bash-like systems, batch renaming every .js file to .tsx looks like this:

find . -name "*.js" -exec rename 's/\.js$/.tsx/' '{}' \;

One amazing thing about TypeScript is that it transparently supports JavaScript files. While our source is now comprised of .tsx/TypeScript files, it can still be pure JavaScript and be 100% functional. This makes it super easy to transition our code over piece by piece.

Compilation

Of course, our application is only configured to handle JavaScript files; it has no clue what TypeScript is or what TypeScript files look like.

This application was originally based on create-react-app–we’ve used the “eject” functionality to create a self-contained React application, with all of our dependencies declared. create-react-app uses Webpack, so we need to change parts of our Webpack config to handle .tsx files. This configuration is based heavily on TypeScript’s own documentation: “React and Webpack”

Update: just-boris on Reddit notes that Microsoft’s own TypeScript-React-Starter repository on GitHub provides a solution for using TypeScript inside of create-react-app.

To begin, we need to install the relevant TypeScript dependencies in our project:

npm install --save-dev typescript awesome-typescript-loader source-map-loader
npm install --save @types/react @types/react-dom

With our dependencies declared, we need to define TypeScript’s config file: tsconfig.json. Our config file will be similar to the one provided by the TypeScript documentation, but with a few modifications:

{
  "compilerOptions": {
    "outDir": "./dist/",
    "sourceMap": true,
    "noImplicitAny": true,
    "module": "commonjs",
    "target": "es6",
    "jsx": "react",
    "allowJs": true
  },
  "include": [
    "./src/**/*"
  ]
}

We’ve updated the target key to es6, which will allow ES6-style JavaScript code. Because this code still goes through the rest of our Webpack compilation process, we can write ES6-like code and have it pass through the existing ES6 configuration that create-react-app provides.

We’ll also add allowJs, which will be set to true. This functionality is what allows us to use pure JavaScript code in our TypeScript files. This is an easy way to slowly transition your code over into TypeScript: if you’re starting fresh with a 100% TypeScript project, you might want to reconsider this.

The most important parts of tsconfig.json are the include and outDir directories: it takes in the src directory (and all the files inside of it, recursively) and outputs to the dist folder. This works for how this ejected create-react-app is configured: you may need to reconfigure these paths for your application.

Finally, we need to update the Webpack configuration itself. For thewidgetcompany, this is config/webpack.config.dev.js.

In the extensions section, we need to add two additional extensions: .ts and .tsx. The final version, on line 77, looks like this:

extensions: ['.ts', '.tsx', '.js', '.json', '.jsx', '']

Additionally, the TypeScript loader needs to be integrated in the loaders section of the config. awesome-typescript-loader will match on .ts and .tsx files, and is added on line 166:

{ test: /\.tsx?$/, loader: "awesome-typescript-loader" }

Finally, we need to update the “entry point” for our application. In thewidgetcompany, this is found in config/paths.js: depending on how your application is configured, this might be found in the Webpack config file as well. Line 71 is updated to:

appIndexJs: resolveApp('src/index.tsx')

With all this set up, we should be able to start our application with npm start and see our first error:

Error in [at-loader] ./src/index.tsx:1:19 
  TS7016: Could not find a declaration file for module 'react'. 
  '/Users/kristian/src/bytesized/thewidgetcompany-eject/node_modules/react/react.js' implicitly has an 'any' type.

Linting

Now the migration process to TypeScript begins. I highly recommend at this point that you integrate a TypeScript plugin into your text editor–I’ve been using Visual Studio Code which works with TypeScript out of the box, and it’s been a great experience. Having TypeScript validation in your text editor allows you to fix various errors without needing to switch back to your build process after each save, to ensure that what you did actually fixed the issues.

If you want to quickly learn a fairly canonical way of writing TypeScript, you might consider adding tslint, a TypeScript linting tool for your code:

npm install --save-dev tslint tslint-loader tslint-react

tslint requires a config file, tslint.json:

{ 
  "extends": ["tslint:recommended", "tslint-react"]
}

Back in our Webpack configuration, in the loaders section:

// Typescript linting
{
  enforce: "pre",
  loader: "tslint-loader",
  test: /\.tsx?$/
},

Restarting our build process will now ensure that .ts and .tsx files pass through tslint before proceeding with the standard TypeScript compilation process.

You might also want to include tslint in your editor–in Visual Studio Code, I had success with vscode-tslint.

Migration

We now have the infrastructure to fully move our application over to TypeScript. Because there will be a number of warnings and errors, I’ll introduce some of the changes and TypeScript concepts in more of a list format, since we’ll be jumping around the application, changing code as needed.

Imports

The default import style used in many React projects (import React from 'react') is not immediately compatible with how TypeScript handles imports. This error manifests itself as the first error we saw above, namely:

Error in [at-loader] ./src/index.tsx:1:19 
    TS7016: Could not find a declaration file for module 'react'. '/Users/kristian/src/bytesized/thewidgetcompany-eject/node_modules/react/react.js' implicitly has an 'any' type.

A previous version of this guide encouraged changing the import style–TypeScript now fixes the need to replace these definitions, via the --allowSyntheticDefaultImports configuration option. You can set this in your tsconfig.json file and remove this error entirely, without needing to change how you import packages.

Interfaces and Types

The most immediate win for most applications moving to TypeScript is defining centralized interfaces and types. Interfaces and types allow you to explicitly define how data will look across your application: it gives your code the strongly-typed definition that the name TypeScript suggest.

If you’re building a more complex application, you should lean on TypeScript’s classes instead. In thewidgetcompany, the application state is very basic, and this would probably be an over-optimization.

There are a number of things in our application that can be defined with interfaces and types. For instance, our entire application state tree:

// types.tsx
interface IApplicationState {
  error?: string;
  failed: number;
  materials: IMaterialSet;
  orders: IOrder[];
  packaged: number;
  shipped: number;
  widgets: IWidget[];
}

interface IMaterial {
  count: number;
}

interface IMaterialSet {
  dowel: IMaterial;
  screw: IMaterial;
  wheel: IMaterial;
}

interface IOrder {
  created: number;
  widgets: number;
}

interface IWidget {
  created: number;
}

export {
  IApplicationState,
  IMaterial,
  IMaterialSet,
  IOrder,
  IWidget,
};

There are a number of simple types in TypeScript–above, we used string and number. A value can be optional, too: error?: string in IApplicationState indicates that the error string can be set to null. Finally, by defining our own interfaces, we can re-use them in defining other interfaces: orders: IOrder[] in IApplicationState indicates that we expect an array of orders, of type IOrder.

The IApplicationState interface is used to ensure that our application state always conforms to this structure, regardless of where it is used in the codebase. For instance, our Redux reducers (vastly condensed in the below code sample) can benefit from making sure that a consistent application state is always returned from functions:

import {
  IAction,
  IApplicationState,
} from "./types";

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

const orderMaterials = (state: IApplicationState): IApplicationState => {
  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: IAction): IApplicationState {
  switch (action.type) {
    case ORDER_MATERIALS:
      return orderMaterials(state);
    default:
      return state;
  }
}

The eagle-eyed among you might have noticed IAction, which didn’t exist in the original types.tsx definition. Below, we’ll add IAction, and begin to use it in our Redux actions:

// types.tsx
interface IAction {
  type: string;
  message?: string;
  order?: IOrder;
}

export { 
  // ...
  IAction,
}

// actions.tsx
import { IAction } from "./types"

export function orderMaterials(): IAction {
  return {
    type: ORDER_MATERIALS,
  };
}

Type definitions

In thewidgetcompany, we use the Redux middleware redux-thunk. redux-thunk provides a “thunk” action that we can use to defer actions. In our Redux actions, this means that there is another type of action that doesn’t exactly fit our IAction interface.

The solution is two-fold: first, we need to import the built-in type definitions for the redux-thunk package, and integrate them as type definitions in our actions. Second, we need to build two types: IDispatchAction, which models how an action is passed to our application as a prop, and IThunkedAction, which defines a function type that looks like a “thunked” action should, in our actions.tsx file.

To begin, we’ll install the types for redux-thunk. You may have noticed at the beginning of this tutorial that we used npm install --save @types/react: this notation installs the relevant type definitions for the specified package. In a much similar way, we’ll do this for redux-thunk:

npm install --save @types/redux-thunk

This adds the type definitions for redux-thunk to our project: our TypeScript configuration should pick it up automatically. For the extra curious, you can see what the type definitions for redux-thunk look like at the project’s index.d.ts.

In our code, we’ll define IDispatchAction, as well as import the new Dispatch type from redux-thunk. This will be used to strongly-type an action passed to a component as a prop:

type IDispatchAction = () => void;

In our App component, we’ll define an IProps interface, which will model how the passed-in props for the component should look. A Component expects a type definition in the format Component<Props, State> – since the App component does not use any component-level state, we’ll set the type definition to Component<IProps, {}>:

import {
  IApplicationState,
  IDispatchAction,
} from "./types";

interface IProps extends IApplicationState {
  attemptWidgetCreation: IDispatchAction;
  checkOrderForPackaging: IDispatchAction;
  generateOrder: IDispatchAction;
  orderMaterials: IDispatchAction;
  shipOrder: IDispatchAction;
}

class App extends Component<IProps, {}> {
  public render() {
    // ...
  }
}

const mapStateToProps = (state: IApplicationState) => state;
const mapActionCreators = {
  attemptWidgetCreation,
  checkOrderForPackaging,
  generateOrder,
  orderMaterials,
  shipOrder,
};

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

IThunkedAction, on the actions.tsx side, uses the Dispatch action provided by redux-thunk to strongly-type a thunked action:

import {
  ADD_WIDGET,
  DOWELS_NEEDED,
  PRESENT_ERROR
  SCREWS_NEEDED,
  WHEELS_NEEDED,
} from "./constants";

import { Dispatch } from "redux";

import {
  IAction,
  IApplicationState,
  IThunkedAction,
 } from "./types";

export function attemptWidgetCreation(): IThunkedAction {
  return (dispatch: Dispatch<IApplicationState>, getState: () => IApplicationState) => {
    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(): IAction {
  return {
    type: ADD_WIDGET,
  };
}

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

Prop interfaces

In our App component, we defined IProps, which modeled what the props passed to our component should look like. In each component in thewidgetcompany, an IProps interface is defined, though they are all less complex than the initial App props:

// Inventory.tsx
import { IWidget } from "./types";

interface IProps { widgets: IWidget[]; }

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

// Shipping.tsx
interface IProps {
  packaged: number;
  shipped: number;
  shipOrder: () => void;
}

class Shipping extends Component<IProps, {}> {
  public 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>
    );
  }
}

These interfaces are specific enough to each component that I’ve kept them in their respective components: if you start to see a pattern emerge in your own code, you might consider pulling these interfaces out into types.tsx.

Conclusion

At the end of our application’s transition to TypeScript, it’s worth asking: Was it worth it?

Consider the following scenario: our “Order” object, which consists of a created date and the number of widgets required to fill the order, now requires an addition customerId identifier to tie it to the customer that ordered it. In a vanilla JavaScript application, we might not feel confident updating the various places that orders are passed and queried. In our TypeScript application, the IOrder interface that we’ve defined gives us confidence that our code will work. If we update our IOrder interface to include a customerId field, we’ll immediately be aware of what pieces of code aren’t satisfying that requirement:

// types.tsx
interface IOrder {
  created: number;
  customerId: string;
  widgets: number;
}

// reducers.tsx
const generateOrder = (state: IApplicationState): IApplicationState => {

  // ERROR: Property 'customerId' is missing in type '{ created: number; widgets: number; }'.
  const newOrder: IOrder = {
    created: Date.now(),
    widgets: Math.floor(Math.random() * 10) + 1,
  };
  // ...
};

Writing TypeScript brings the best part of type-safety to your web apps: knowing that something will break, before it actually does. To answer our previous question, our transition to TypeScript was worth it–we’ve reached a point where we can confidently continue to build on our application.

The complete source code to accompany this blog post can be found on Gitlab: bytesized/thewidgetcompany-typescript.

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.

The best way to move to the Colemak keyboard layout

The original QWERTY keyboard layout is inefficient. It was developed in 1873 – originally for typewriters, but as newer technology was manufactured, it stuck as the layout of choice.. It’s been over a century and we’re still using it. This is because switching is really hard. It’s essentially the same as a piano player trying to play a piano in which every single key has been switched.

I like the Colemak layout. It’s a practical layout comparatively to the “fastest”1 keyboard layout, Dvorak. There are seventeen keys moved from QWERTY to Colemak (versus Dvorak in which, from what I can tell, every key is changed), so it’s not a complete change, but still pretty comprehensive.

I’m not going to go into too much detail about why you would switch; personally, I was having RSI problems and switching to Colemak seems to have helped a great deal. Jason Trill has a great post on the ErgoDox keyboard that includes great keyboard heatmaps – a representation of how typing is distributed on the physical keyboard. The below example shows a large Javascript framework being typed on each layout.

QWERTY:

qwerty heatmap

COLEMAK:

colemak heatmap

DVORAK:

dvorak heatmap

The more keys that can remain on the home row, the better. While Dvorak seems to have the upper hand, I’ll defer to the 80/20 rule–if we can get a large portion of the benefits of a new keyboard layout by doing a less intensive transition to Colemak, that’s a win in my book.

Switching is hard! If you’d like to retain a reasonable amount of your typing speed, switching cold turkey to Colemak can be brutal, and dare I say, traumatic. Everyday typing becomes an infuriating experience!

Enter Tarmak. Tarmak is an ingenious system devised in the Colemak forums, which acts as a “transitional” layout system from QWERTY to Colemak. Here’s a nice GIF from the introductory forum post that sums it up well:

You shouldn’t expect to be at maximum typing speed immediately – after about two months, I still had some inaccuracies day-to-day. When should you switch? Here’s a nice answer from the Colemak forum:

I think focusing on accuracy is very important when considering when to level up. From my experience I suggest the aim should be between 95-100% combined with a speed of >25WPM. Having a greater speed initially helps, as performance will very likely decrease when adding more keys from the colemak layout.

If you’re looking for typing tests to score your WPM (words per minute), there’s lots of online options. I used GNU Typist–if you’re on Mac or Linux, it’s easy to install and navigate around, even as you learn a new layout. Because it was in the command-line, it was easy to hop in and do focused typing practice every day, while still continuing my normal coding patterns. Of course, just being on the computer at all is good practice!

The forum includes various downloads for most operating systems. I used this Github project that adds Tarmak options in the native OS X keyboard layouts. I was happy to see that once I finished Tarmak, Mac OS X had Colemak built-in. You can find all the various layouts in “Input Sources” under the Keyboard section of System Preferences.

One thing you may want to consider is how to transition between multiple machines. I have a work laptop and a personal laptop, and it was important to keep those machines synced on the same Tarmak transitional layout. I handled this by scheduling a calendar event where I would open both machines and change the layouts at the same time–of course, you’re welcome to implement whatever system works best for you.

Unfortunately, iOS doesn’t have Colemak support for day-to-day typing, but does have it for Bluetooth keyboards. I find that I type fine on my phone still, but interestingly enough, I revert back to Colemak sometimes when I’m not looking.

Overall, the move to Colemak seems to be a win so far – my wrists feel a bit better, and it’s another item checked on my ergonomic to-do list.

Integrating Mixpanel into a React and Redux application

I did some work recently on integrating Mixpanel into a project I’ve been building. Integrating Mixpanel was a lot easier than I thought, but there were a couple complicated bits in getting it to work as expected with a React / Redux application. Here are the steps:

Installation

npm install --save mixpanel-browser

Import the mixpanel-browser library and initialize it somewhere near the beginning of your app’s component lifecycle. I did this in my AppContainer (using the usual React component lifecycle methods), which renders out the router and components:

import mixpanel from 'mixpanel-browser'

// ... inside component
componentDidMount () {
  mixpanel.init('your_mixpanel_token')
}

Identification

Identify your user and pass any additional information (name and email, for instance). This can be a tricky one depending on how your application’s redux flow is structured–it would make the most sense to do it as part of an action, but depending on your app’s concerns (persistence being a primary issue for my app), it might need to also happen in the reducer. Here’s how it’d look in an action:

export const loginUser = (data) => {
  /* Log in user via ajax...
   * With success 'json'...
  */
  const { user } = json
  mixpanel.identify(user.id)
  mixpanel.people.set({ '$email': user.email })
}

In my application, the store is persisted into local storage using redux-persist, so in the reducer, separately handling an already-logged in user via the store rehydrating looks like:

import { REHYDRATE } from 'redux-persist/constants'

// in your reducer, handling the REHYDRATE type:
case REHYDRATE:
  // Pulling the 'user' object from the user store
  const user = action.payload.user.user
  mixpanel.identify(user.id)
  mixpanel.people.set({ '$email': user.email })

  return state

Tracking events

With an identified user, you can begin tracking events in your actions:

export const doAThing = () => {
  mixpanel.track('Do a thing')
  return {
    type: DO_A_THING
  }
}

If you need to act on the tracking event finishing, you can wrap the mixpanel call in a Promise and chain it:

const trackEvent = (action, options) => {
  return new Promise((resolve, _) => {
    mixpanel.track(action, options, response => resolve(response))
  })
}

trackEvent('Do a thing').then(response => _doMoreThings(response))

Overall, I’m pretty happy with how the Mixpanel + React integration turned out. The user identification feels a little strange with redux-persist, but when doing the identification as part of the login and sign up actions, it feels more intuitive than it would be than firing it as part of a button click callback.

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”.

One year with React

This is the final post in Bytesized’s A year with React.js series, which explores increasingly complex aspects of being a React developer. This post covers your first year with React components––component testing, future-proofing, and introducing data flow to your application, with Redux. It’s strongly recommended that readers start this series from the beginning, with the first post, “One hour with React”.

In this series, we’ve covered the basics of React components, but we pretty quickly moved past the basics of rendering HTML and writing component classes into the more complicated parts of the framework: component lifecycles, structuring your application, and looking towards the future with high-quality open-source tooling. In this final part of the series, we’ll continue to delve more into what it means to build a React application, one year in, that feels well-crafted. It might seem like over-optimizing to worry so much about well-crafted code. Clean Code, referenced in the previous post, has this to say about code that isn’t well-crafted:

If you have been a programmer for more than two or three years, you have probably been significantly slowed down by someone else’s messy code. If you have been a programmer for longer than two or three years, you have probably been slowed down by messy code. The degree of the slowdown can be significant. Over the span of a year or two, teams that were moving very fast at the beginning of a project can find themselves moving at a snail’s pace. Every change they make to the code breaks two or three other parts of the code. No change is trivial. Every addition or modification to the system requires that the tangles, twists, and knots be “understood” so that more tangles, twists, and knots can be added. Over time the mess becomes so big and so deep and so tall, they can not clean it up. There is no way at all.

As the mess builds, the productivity of the team continues to decrease, asymptotically approaching zero. As productivity decreases, management does the only thing they can; they add more staff to the project in hopes of increasing productivity. But that new staff is not versed in the design of the system. They don’t know the difference between a change that matches the design intent and a change that thwarts the design intent. Furthermore, they, and everyone else on the team, are under horrific pressure to increase productivity. So they all make more and more messes, driving the productivity ever further toward zero.

Every programmer would like to think that their code is well-crafted. But bad code doesn’t happen with huge, sweeping decisions about features. It happens each time we skimp on cleanly separating logic between modules. It happens when we leave a variable poorly named, or when we don’t implement error checking because it takes just a bit too long. It’s easy to point at a particularly complicated feature that was implemented quickly and say “This is why our application is bad!” My experience has always been quite the opposite: it’s when we revisit an old part of our application and decide that it’s not worth it to use the same coding style as our new work; when we see lazy code in code review and make a mental note to re-think it later, but don’t ever leave actual time to handle tech debt.

This problem has come to a head in what is now likely the most popular and fastest growing language in the world: JavaScript. The unique combination of the explosion of the internet and the deceptively simple style of the language has made web development a singularly approachable avenue for new programmers. The result is a generation of code that missed Clean Code; one where shipping is prioritized over crafting something with a long life.

Future-proofing

JavaScript is infamous as a fast-moving language. It’s a common jab on Hacker News to suggest that because a new hobbyist re-implementation of React has been released on GitHub, that every React team will drop everything and move to the new hotness. Consider Vue.js. In the last few years, Vue.js has become known as “the React killer”. Of course, React itself was “the Angular killer” or “the Ember killer” only a couple years ago! Vue itself is closer to Angular in design, but because of React’s current status as the most prominent or hyped framework in the JavaScript developer community, Vue has been touted as the next hot thing.

So while I audibly groan when I see someone make fun of JavaScript developers for jumping from bandwagon to bandwagon, this joke has its merits: the community has seen a huge amount of innovation in the last decade, and being a JavaScript developer in 2017 means something quite different than what it meant in 2007. A JavaScript developer pre-Node, pre-Angular, pre-React was someone using jQuery or something similar to enrich their HTML and CSS websites. When AJAX came along, JS developers began scratching the surface of fully interactive applications. In 2017, a JavaScript developer could be entirely removed from the browser, the application JS was designed for! A Node developer is handling the same complexity that backend developers were fifteen years ago, but with a programming language built with entirely different concerns.

It’s because of this unique combination of JavaScript’s increased scope and responsibility in the programming community and, frankly, its shortcomings as a language, that makes the idea of “future-proofing” your JavaScript applications such a pressing concern. It’s hard to say what the future of the language is: a few years ago, many JS developers were ready to move entirely to CoffeeScript full-time. When ES6, the newest version of JavaScript, began making its way into the hands of development teams, tools like Babel replaced CoffeeScript seemingly overnight. The developer experience for JavaScript can change constantly. I’m sure that at this point, even I’m a bit behind on what the latest and greatest is! But how does this affect React? Assuming that we stick with React and don’t jump ship to the hottest new framework, is this idea of churn, or the rate of change in technologies and frameworks, going to cause our React apps to stop working overnight?

The truth is that React has remained quite consistent in its lifetime. The original premise of React–providing a performant rendering engine for lightweight components–has stayed constant from version 0.1 to version 15, the current version at time of writing. The definitions and naming, like all software projects, saw some changes in the first couple years. Facebook’s wonderful “Our First 50,000 Stars” explores the first three-and-a-half years of the project, and acknowledges this problem–no doubt because of React’s status in the JS community, this rollout process was closely examined, and perhaps mocked by the community:

During the first year of React, internal adoption was growing quickly but there was quite a lot of churn in the component APIs and naming conventions:

  • project was renamed to declare then to structure and finally to render.
  • Componentize was renamed to createComponent and finally to createClass.

Notably, the render method, arguably the most important method for React components, went through four iterations to settle into what it is today. These kind of changes are to be expected in the initial life of a project. Compare that to the increasingly detailed and helpful CHANGELOG file for React releases, which almost always contains many multiples more of deprecation warnings and warnings over actual breaking changes. The framework itself is quite stable!

As for your applications themselves, building applications that can weather the storm of JavaScript churn can almost be reduced to simply focusing on writing clean, maintainable code. Given the immensity of the JavaScript developer community and ecosystem, it’s not surprising that there are dedicated resources to this very topic. Addy Osmani’s “Learning JavaScript Design Patterns” is a great resource, and also is entirely free on his website. Facebook’s own “Thinking in React” serves as almost a bible on the React state of mind. React, of course, also benefits from an incredibly active developer community. Projects like React Newsletter collect some of the best articles and projects in the React community every week, and can be thought of as taking the pulse of the current state of the ecosystem.

Component testing

Clean code, as we discussed in the previous post in this series, is about building things that work long-term. There is likely no better strategy for testing the long-term viability of your application than testing. Writing tests for your components is the best thing you can do to stabilize your application, and the React ecosystem provides an incredible variety of really effective tools to help writing tests not be a chore.

The most prominent of these is Jest, created by Facebook. Jest is designed to work out-of-the-box: in a world of complicated testing tools and developer experience, Jest is a breath of fresh air and makes beginning to write tests trivial. To confirm this, we’ll build a component using Test-Driven Development, and ensure that as we update it, the tests continue to pass, and provide us a stable application.

Earlier in this series, we used the concept of a blog application to model various components–the most important of these being, as you can imagine, a Post. A post, in our simple example, consists of just a title and content.

We’ll use the tool create-react-app to prototype components and their associated tests in this section. I’ve created a GitLab repo where you can download the source code–given a functioning npm install, you should be able to run npm install and npm test to run the tests locally. In the interest of brevity, I’ve removed import statements and npm install directives from the below code samples: please check the GitLab repository for a pre-made package.json file, and components with all the necessary modules imported. As we proceed through this example, relevant pieces of code will also be included below.

Post and HeaderText

class Post extends React.Component {
  render() {
    return (
      <div>
        <h1>Post title</h1>
        <p>Post content</p>
      </div>
    )
  }
}

To begin testing this component, we should first clarify what the desired behavior is. In the above example, there are two important pieces to the post: the title, and the content. Given the file Post.js, we’ll create a matching Post.spec.js test file, and populate it with our first test:

import React from 'react'
import ReactDOM from 'react-dom'
import Post from './Post'

it('renders a post', () => {
  const container = document.createElement('div')
  ReactDOM.render(<Post />, container)
})

Running npm test should confirm that the tests pass. For most machines, this command will setup functionality to re-run tests whenever a file changes in this directory: if this isn’t working for you, simply re-run npm test whenever we mention updates to files or tests.

In this simple test, we’ve just ensured that the Post component renders; it doesn’t check for any particular data, just that it doesn’t “blow up”, because of syntax errors or similar issues.

In the previous parts of this series, we’ve explored breaking down components into sub-components, for later re-use. To begin doing this, we’ll remove the h1 tag from Post, and replace it with a HeaderText component, which accepts text to display via the title prop.

class HeaderText extends React.Component {
  render() {
    return (
      <h1>{this.props.title}</h1>
    )
  }
}

class Post extends React.Component {
  render() {
    return (
      <div>
        <HeaderText title="My title" />
        <p>Post content</p>
      </div>
    )
  }
}

With the addition of HeaderText.js, we can also add specs, at HeaderText.spec.js, to ensure that the component renders as expected:

it('renders header text', () => {
  const container = document.createElement('div')
  ReactDOM.render(<HeaderText title="My title" />, container)
})

Snapshots

One of the most intriguing features recently added to Jest is snapshots. Snapshots read the structure of your component at the time of testing, and write the structure to a JSON file in your test directory. In doing this, you essentially get a free test in the future–a simple check to compare the new structure at the time of the test running, to the previously stored component structure. We’ll add tests to Post and HeaderText by including the renderer function from the react-test-renderer package, capturing the component structure, and matching it:

// Post.spec.js
it('matches the snapshot of a post', () => {
  const tree = renderer.create(<Post />).toJSON()
  expect(tree).toMatchSnapshot()
})

// HeaderText.spec.js
it('matches the snapshot of header text', () => {
  const tree = renderer.create(<HeaderText title="My title" />).toJSON()
  expect(tree).toMatchSnapshot()
})

This additional test should pass without incident. To test the snapshot behavior, we can change the title prop passed in:

// HeaderText.spec.js
it('matches the snapshot of header text', () => {
  const tree = renderer.create(<HeaderText title="My new title" />).toJSON()
  expect(tree).toMatchSnapshot()
})

In doing so, we can see that the new structure does not match the previously stored snapshot:

FAIL  src/HeaderText.spec.js
 - matches the snapshot of header text

   expect(value).toMatchSnapshot()

   Received value does not match stored snapshot 1.

   - Snapshot
   + Received

   @@ -1,3 +1,3 @@
    <h1>
   -  My title
   +  My new title
    </h1>

     at Object.<anonymous>.it (src/HeaderText.spec.js:20:16)
     at process._tickCallback (internal/process/next_tick.js:103:7)

This failure, luckily, is expected. In this case, we can simply press the “u” key, to update our failing snapshots. Given a different instance, this test might be unexpected, and simply having a snapshot saved us from headaches in the future.

Content

Finally, we’ll implement the Content component, which will accept a text prop and be the body of our blog post:

class Content extends React.Component {
  render() {
    return (
      <p>{this.props.text}</p>
    )
  }
}

Our Post component now contains two sub-components, each accepting dynamic values via props:

class Post extends React.Component {
  render() {
    return (
      <div>
        <HeaderText title="My title" />
        <Content text="My post content. Testing, testing!" />
      </div>
    )
  }
}

We’ll essentially copy and paste over the tests for Content.spec.js, building a simple “it renders” test, and a snapshot test:

it('renders content', () => {
  const container = document.createElement('div')
  ReactDOM.render(<Content text="My post content. Testing, testing!" />, container)
})    

it('matches the snapshot of content', () => {
  const tree = renderer.create(<Content text="My post content. Testing, testing!" />).toJSON()
  expect(tree).toMatchSnapshot()
})

Our new Content test passes, but now our previous Post snapshot test fails! This is, again, to be expected: internally, the structure of the Post component hasn’t actually changed (Content is, after all, just a p tag), but the text has changed. We’ll update the snapshots again with the “u” key.

Finally, I’d like to add one more function to the HeaderText component–the ability to pass in a CSS class name, to style the h1 tag depending on where it is being used. For instance, header text for a blog post will be much different than the header text used for a sidebar, or for the title of the blog itself. Let’s add an additional cssClass prop to HeaderText, passing it into the h1 tag or defaulting to the empty string "" if no cssClass is received:

class HeaderText extends React.Component {
  render() {
    return (
      <h1 className={this.props.cssClass || ""}>{this.props.title}</h1>
    )
  }
}

(Note-this code could be refactored by using React’s defaultProps functionality, to set cssClass to an empty string if it isn’t passed in directly. I’ll leave this as an exercise to the reader.)

When we save this file, we should see two test failures. Can you guess what they are? Both the Post component and the HeaderText component now fail, given that the structure of the h1 tag in HeaderText now includes a className prop. Update the snapshots to see that the tests should pass again.

We should actually ensure that the prop cssClass is being passed through. To do this, we’ll make use of the tool enzyme, a collection of React testing utilities from Airbnb. The most useful part of enzyme is the shallow function, which allows unit testing of individual components, as opposed to the entire child structure of a component. From the project’s README:

Shallow rendering is useful to constrain yourself to testing a component as a unit, and to ensure that your tests aren’t indirectly asserting on behavior of child components.

In this case, shallow rendering will allow us to simply check the first layer of HeaderText: although it doesn’t have any child components now, this may change in the future. With our shallowly-rendered component, we can find the h1 tag and confirm that the cssClass prop has been passed to the h1 tag as a class:

it('passes the cssClass prop to the header text class', () => {
  const component = shallow(<HeaderText cssClass="post-title" title="My title" />)
  expect(component.find('h1').hasClass('post-title')).toBe(true)
})

These tests give us a great starting point to continue to build out our blog application. As we add new features or components, our snapshots in particular will indicate how the application is changing from a UI perspective: we can either update the snapshots when known and expected test failures occur, or fix accordingly when a failed snapshot comparison is surprising or unexpected. Additionally, adding this testing process to a continuous integration tool, or as part of your code review process, can be an incredibly effective way to work with a team and build stable applications.

Introducing data flow to your application

Prototyping an application in React is really easy–building static components to template out how things will look can be done in even a matter of hours. When you begin to add dynamic pieces to your application that require retrieving and sending data, it’s very likely you’ll end up exploring tools like Redux or MobX. Both of these tools deal with the problem of managing state. State can be simple: instead of our above Post example having a hard-coded title and content text, it could be a JavaScript object:

{
  title: "My post title",
  content: "My post content"
}

A collection of posts is no different–instead of a single JavaScript object, our state becomes an array of objects:

[
  {
    title: "My post title",
    content: "My post content"
  },
  {
    title: "My newer title",
    content: "My newer post content"
  }
]

Dealing with this kind of state is straightforward: you could probably figure out how to render a collection of Post components fairly intuitively given this state, maybe doing something like:

const posts = getState(posts)
return posts.map(post => <Post title={post.title} content={post.content} />)

The above code, with the exception of the abstracted getState call, just creates a collection of Post components, filling in each title and content prop with the relevant field from the current post.

Given a more complicated application, managing state isn’t so clear. Consider this example, adapted from a React-based task manager application I was building last year:

{
  tasks: [
    {
      id: "5ec862a1-caca-4b45-b594-1fad523bc046",
      title: "Take the garbage out",
      context_id: "9a1d3479-ff8a-4f54-ae33-ffbca6b81760",
      project_id: "c1547e80-db77-4d32-8a80-00a8cac55122",
      created_at: "2017-03-12T09:14:43+00:00",
      updated_at: "2017-05-14T10:23:01+00:00",
      due_at: "2017-05-15T22:00:00+00:00",
    }
    // ... additional tasks
  ],
  contexts: [
    {
      id: "9a1d3479-ff8a-4f54-ae33-ffbca6b81760",
      name: "Home",
      created_at: "2017-03-01T09:10:01+00:00",
      updated_at: "2017-03-01T09:13:45+00:00",
    }
  ],
  projects: [
    {
      id: "c1547e80-db77-4d32-8a80-00a8cac55122",
      name: "Daily tasks",
      created_at: "2017-03-02T12:35:15+00:00",
      updated_at: "2017-03-02T12:35:15+00:00",
    }
  ],
  config: {
    hideSidebar: true,
    shiftReturnToConfirm: false
  },
  user: {
    authentication: {
      token: "<token>",
      expires_at: "2017-05-21T00:00:00+00:00",
    },
    id: "b9369559-ed1f-40a1-ab7f-32749766f351",
    name: "John Smith",
    email: "[email protected]",
  }
}

This example contains a great deal of complexity: the state itself can be represented as an object, but even one level deep we can already see a combination of arrays (tasks, contexts, projects) and objects (config, user). Abstracting getState before seemed easy; now, it’s worrying to not know how we’re retrieving state. Let’s briefly touch on Redux, to see how the basics of how it would handle managing something like the above state.

The first thing you’ll notice, out of neccesity, is the sheer size of the above state object. This object, condensed in the above example, contains all of a user’s tasks, projects, and contexts for a Getting Things Done application, as well as the authentication and configuration logic for the user themselves. For even a light user of this application, the state object consumed by the React application is quite large.

Redux is built to contain that state, and interface with it in a predictable way. In fact, predictability is likely the most important element of Redux, with the project’s homepage referring to it almost immediately:

I wrote Redux while working on my React Europe talk called “Hot Reloading with Time Travel”. My goal was to create a state management library with minimal API but completely predictable behavior, so it is possible to implement logging, hot reloading, time travel, universal apps, record and replay, without any buy-in from the developer.

Redux introduces a new aspect to React components–the concept of containers. Containers are components that interface with the application’s Redux store, which contains the application state. A Tasks container, for instance, would retrieve all of a user’s tasks from the store, and use them to render the individual Task components.

If you’re familiar with Getting Things Done or are just particularly quick, you might also notice that a task has a corresponding context and project. Therefore, it isn’t enough just for a Tasks container to know about tasks; it also needs to know about projects and contexts. Clearly, the problem of state management becomes complex almost immediately.

Redux is designed to be unidirectional: data flows a single direction, down. This means that data isn’t passed back and forth between components–a TaskForm component, where a user can edit a task’s name or other information, isn’t the “source of truth” for that task’s data across the application.

Imagine a simple Task component, with a checkbox to indicate the task is complete:

When you update the task to complete, where does the fact that this task is now complete get stored? Do other components, for instance, one that indicates how many incomplete tasks are available, now know that this task is complete? To solve this, there are two potential solutions:

  1. Each component listens to every other component, as needed. A IncompleteTasks component, which shows how many tasks are left to complete, listens to every Task component. When a Task component performs an update, every component watching that component also updates as needed.
  2. Each component receives state as needed, and when a change happens, any relevant updates come through the entire component structure, causing re-renders as needed.

My gut feeling when I began using Redux was that the first option was faster. Re-rendering sound expensive, after all! It turns out that Redux, which is described by the second option, is not only incredibly efficient, but a lot easier to reason about, and to work with on a day-to-day basis.

We’ve spent a lot of time talking about component trees: a Blog component contains many smaller components. In the same way, Redux state can be thought of as basically a tree, since it fundamentally is just a JavaScript object. Because of this, putting together components and state is like tying certain parts of the app to certain parts of the state tree. The aforementioned Tasks container needs to know about tasks, projects, and contexts: when we initialize the Tasks container, we simply pull out those parts of state, and pass them in as props:

class Tasks extends React.Component {
  retrieveProjectForTask(task, projects) {
    return projects.find(projects => project.id == task.project_id)
  }

  retrieveContextForTask(task, contexts) {
    return contexts.find(context => context.id == task.context_id)
  }

  render() {
    return this.props.tasks.map(task => {
      return (
        <Task
          context={this.retrieveContextForTask(task, this.props.contexts)}
          project={this.retrieveProjectForTask(task, this.props.projects)}
          task={task}
        />
      )
    })
  }
}

(Side-note: the savvy among you will recognize the above code as very inefficient. Don’t worry: I wouldn’t write something like this in production, and would substitute the retrieveProject/ContextForTask functions for a more efficient, likely hashtable-based solution. Take a deep breath!)

Our container knows about state, and knows what things the Task component needs to properly render. Instead of Task also needing to know about state, and how tasks, contexts, and projects all fit together, it simply receives task, context, and project, and takes what it needs from them:

Updating a task is conceptually simple, but perhaps out-of-scope for a detailed explanation at this point in the series (good thing we have a Redux series too–see below!). The gist is this: an action is passed into the Task component, which, when called, updates the Redux store at the top-level. When the update is successful, the new version of state is passed all the way down the component tree, ultimately resulting in the Task component that called the original action being updated. Instead of having components watch each other, all components, keeping in the common React theme, simply the output of a render function and their input, which is the updated state.

Looking for a deeper dive into Redux? Still not quite clicking? Be sure to check out our new Redux introduction series, Redux manufacturing.

Conclusion

This series has explored some of the lesser-talked about parts of React–there are a number of tutorials covering the basics of components, and build tooling, but not as many about why React is built the way it is, or how it lends itself to sturdy, long-lived code. This series is somewhat abstract in that way; we’ve looked at actual code in a variety of situations, from the basics, to writing tests, to our most recent peek at why we would consider Redux in more complex applications.

Still have questions? Check out the comment section below, or engage with the truly excellent React.js community on Twitter, GitHub, Stack Overflow, and a number of other places.

Want to receive this course in your email inbox? Join the A year with React.js mailing list–it’s instant and easy.

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

One month with React

This is part three in Bytesized’s A year with React.js series, which explores increasingly complex aspects of being a React developer. This post covers your first month with React––application structures and open-source libraries. It’s strongly recommended that readers start this series from the beginning, with the first post, “One hour with React”.

In the previous post in this series, we explored React components that render other components. In doing so, we begin to come up against the question: what does a well-crafted React application look like?

Exploring the concept of “good” code is a topic for another time, but most programmers know bad code when they see it. If they can’t sense it immediately, they will as they begin to work on it; that feeling of helplessness, where it’s unclear where to even begin with a new feature, or how to fix something that isn’t working. In this post, we’ll explore a single facet of good code–the concept of structure.

Well-structured code gives developers a vantage point into an application. With a well-structured application, it is clearer where a new feature should be built. It is more clear how an old feature is failing. A good structure, in short, lends itself to maintenance, not creation. The seminal programming book Clean Code has this to say on maintenance:

…in the auto industry, the bulk of the work lies not in manufacturing but in maintenance—or its avoidance. In software, 80% or more of what we do is quaintly called “maintenance”: the act of repair. Rather than embracing the typical Western focus on producing good software, we should be thinking more like home repairmen in the building industry, or auto mechanics in the automotive field.

If you’ve ever worked on a software project for more than a couple years, you’re likely intimately aware of the phenomenon of encountering your own code, and not understanding it. If it isn’t your own code, and it’s a departed team member, the situation can be even worse: it still doesn’t make sense, and the person who wrote it isn’t around to answer questions! Maintainable code becomes a prime concern after a short amount of time in an application, and this is no different with React applications.

Application structures

React is often referred to as simply “the view layer”. Most applications, whether web or anywhere else, follow a similar implementation pattern. There is code to produce data, to load it, to display it; the Model-View-Controller (MVC) pattern is common among many programming languages. Regardless of how it’s implemented, there is often a point in a software project where you have data that needs to be shown to a user.

Other web frameworks differ in complexity from React; Ember, for instance, provides the entire spectrum of tools needed for a web application. Version 1.0 included not just Handlebars templating for the view layer, but powerful controller and model tooling. The same story has applied to many web frameworks: it’s very easy to think of a web framework as incomplete if it doesn’t handle the entire end-to-end process of retrieving and displaying data.

React takes a different approach by firmly providing just views. Data can be passed in, sure, but React provides no built-in tooling for manipulating or loading data, besides what JavaScript itself contains. Want to load data? Check out Facebook’s GraphQL library, which has rethought the concept of retrieving data via REST. If your data is already defined and available, the popular Redux library can store your data and inject it into your React components.

(Be sure to check out our new Redux introduction series, too! Redux manufacturing)

I’ve referred to React’s ability to change how you write and think about code in every post in this series. React is a wonderful tool because it is obsessively simple. In being simple, it forces you to make changes to how you structure and reason about your application, and about the data that it needs. Redux is similar: data flows a single direction, so the way that you think about how data should move in your application will be permanently changed. In both these situations, is this the end-all, be-all, perfect solution to building applications? It’s hard to say–it does, however, paint an incredibly compelling and straightforward path to take in the future.

React components are functional by design–given input, we can always expect the same output from our render function:

output = render(input)

This line of code almost looks like an equation. Building out React component trees, like we did in the previous post in this series, is like combining many render equations into a single application:

header = render(title) + render(menu)

post = render(post_title) + render(post_date)
content = render(post) + render(post)

application = header + content

In building applications, React becomes somewhat of a game, as we explored with component trees: how modularized can we make this project? At what point does the application stop looking like a bundle of HTML, and more like a collection of highly-independent components?

Consider React Native, Facebook’s project to bring React to mobile devices. There are a number of stories of successful React Native launches targeting iOS and Android: most notably, Facebook’s own Ad Manager re-used 85% of component code between platforms. That insane level of re-use happens when we try and build maintainable, re-usable code; by building component trees that are highly modularized.

Structuring your React application can be as simple as a single flat directory of components. Our blog component tree from the last example might serve as a good reference:

Blog
  Header
    MenuItem
      Link
    MenuItem
      Link
  Posts
    Post
      PostDate
        SmallHeaderText
      PostTitle
        HeaderText
          Link
      PostContent
        Paragraph
          Link
        Paragraph
  Sidebar
    SmallHeader
      List
        Link
        Link
    SmallHeader
      List
        Link
  Footer
    Paragraph
      Link
    Paragraph
      Link

While there seem to be a number of highly-nested components in this structure, we can begin to see a more reasonable structure by flattening and removing duplicates:

Components/
  Blog.js
  Footer.js
  Header.js
  HeaderText.js
  Link.js
  List.js
  MenuItem.js
  Paragraph.js
  Post.js
  PostContent.js
  PostDate.js
  Posts.js
  PostTitle.js
  Sidebar.js
  SmallHeader.js
  SmallHeaderText.js

This list of components not only provides what we need for our blog application, but it can also provide components for other applications as well. I’ve seen many examples of multiple React applications re-using the same internal components–this provides consistency and rigor around how different teams or different projects are maintained by a larger organization. For instance, we might pull out the more general components not specific to the blog application, and package them as an internal “styleguide” for a company:

CompanyStyleguide/
  Components/
    Header.js
    HeaderText.js
    Link.js
    List.js
    Paragraph.js
    SmallHeader.js
    SmallHeaderText.js

In building this internal package, we could even begin to include more complex components, like Header, and extend or build off of them with custom solutions or by providing the ability to pass in options:

class BlogHeader extends React.Component {
  render() {
    return (
      // Component included from CompanyStyleguide package
      <Header backgroundColor="ffffff">
        <HeaderText color="000000" text="My Application" />
      </Header>
    )
  }
}

This is only scratching the surface of application structure in your projects. Again, we’re striving for maintainable, re-usable code: because React components are functional and straightforward, they lend themselves towards building these kind of modularized projects.

Open-source libraries

But what does this look like in practice, in a real React application? To explore this, we can turn to the wonderful world of open-source. There are a massive number of open-source components for React out in the world, and some of the most popular ones will exemplify good design by nature of being in use by huge numbers of developers and applications.

Some of the most popular open-source React projects repurpose existing design frameworks as React components. The material-ui project is one of these: with over twenty five thousand GitHub stars at time of writing, it’s clear that it’s an incredibly popular project in use by many applications.

Structurally, it looks like you might expect. A src folder designates the location of components, and each component is laid out in its own directory with optional tests (a topic we’ll cover in the next post):

material-ui/
  src/
    Checkbox/
      Checkbox.js
      Checkbox.spec.js
      index.js

The Checkbox component itself is fairly straightforward, with the exception of the number of options that can be passed in via props. Positions, styles, and behaviors are all customizable, and the component’s propTypes definition, a topic we covered in the previous post, is a good introduction into how you might override some of the component’s default behavior.

Interestingly enough, the Checkbox component’s render function reveals that the component does not return plain HTML. Like the best React projects, material-ui abstracts and modularizes the component, using the subcomponent EnhancedSwitch:

class Checkbox extends React.Component {
  render() {
    return (
      <EnhancedSwitch
        {...other}
        {...enhancedSwitchProps}
      />
    )
  }
}

The EnhancedSwitch component is stored in an internal directory, which handles the actual HTML rendering of components. In the render method of the EnhancedSwitch component, a collection of inputs, labels, and event handlers are collated into a single wrapped div:

class EnhancedSwitch extends React.Component {
  render() {
    return (
      <div ref="root" className={className} style={prepareStyles(Object.assign(styles.root, style))}>
        <EventListener
          target="window"
          onKeyDown={this.handleKeyDown}
          onKeyUp={this.handleKeyUp}
        />
        {inputElement}
        {elementsInOrder}
      </div>
    )
  }
}

We can see that in popular, highly-used React projects like material-ui, this modularized method of development highly lends itself to abstraction. It’s a practice that might seem unnecessarily verbose at first, but as projects grow in scale, it will continue to pay off dividends.

material-ui is just one of many incredibly useful React open-source libraries. The following are a couple of wonderful projects, that I can vouch for personally saving me time and frustration in my React applications. You should check them out not only to save you from duplicating functionality that has been battle tested by other users, but also to see what highly functional React components look like out in the world.

  • react-select: this project takes some of the best features of powerful JS select libraries and modernizes them as a powerful React component. I’ve used this library in almost every React application I’ve built–the onChange handler behavior and built-in support for multiselect, searching, and async tooling make react-select a first-class solution worth dropping into any project.
  • redux-form: well-behaved forms are deceptively hard to implement in React. While HTML5 has introduced a number of great behaviors for inputs, from requirement (this field must be filled out) to “type-checking” (this field can only be a number), support is browser-specific and may need to be more complex. redux-form does all of this and more, allowing form data to be passed through and handled by redux and wrapping form submission behavior in smarter, safer tooling. redux-form has a bit of a learning curve, but it has the ability to handle almost any form situation you throw at it. I’ve built a number of complex forms with redux-form, and found that even the basic functionality is so much better than the simple implementation I would have tried to ship an application with–it’s worth taking a look at, even just to consider what things you might have forgotten to include in your form code.
  • react-dnd: I wish that this stood for Dungeons and Dragons, but drag-and-drop functionality is cool too. Drag-and-drop is an interesting example of a JS feature that can break how you reason about your code: it’s so platform-specific and is firmly in the territory of jQuery and DOM-based libraries that including it into a React application can make components do a lot more than they really should. As a rule of thumb, when you begin to put jQuery functions in a componentDidMount hook, you should explore new approaches to that problem. react-dnd handles this as most advanced React components do: your original component becomes wrapped in a “higher-order component”: a component that returns another component. This familiar pattern often takes the form of a specific library function that returns your wrapped component–in react-dnd, this looks like connectDragSource(<YourComponent />). react-dnd is a glimpse at how interactive JS applications can still be written in a functional, performant way, without scattering jQuery handlers haphazardly through your application.

Programmers famously are lazy. We often say “Don’t Repeat Yourself”, and that code duplication is a “code smell”, and a sign of danger. This ethos has been only bolstered by the availability of high-quality open-source libraries.

You might be a really confident programmer and decide that writing your own datepicker component is a good usage of your time. As you begin to test it in different browsers (you are doing that, right?) you’ll quickly find that the CSS solution you had in mind probably doesn’t scale. Have you checked it out on mobile? Uh oh. These kind of concerns are handled when you use a project like react-dates: battle-tested not just by Airbnb, but developed with over fifty contributors coming from a variety of backgrounds and applications.

Want to receive this course in your email inbox? Join the A year with React.js mailing list–it’s instant and easy.

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

Full-text search with Phoenix and PostgreSQL

I recently built an Elixir/Phoenix job board–I’m a big fan of the ecosystem and I wanted to prototype a little project that benefited the community at large. One useful aspect of a job board is searching – being able to query for “Los Angeles” and returning jobs that are actually in Los Angeles, instead of scrolling through a list looking for each location.

Phoenix uses PostgreSQL by default, which is great for building a search solution. While Postgres has a full documentation section about full-text search, I wanted to quickly give an overview of how I specifically added full-text search to my Phoenix application.

(I want to note here that in my instance, I’m searching across a single model Job – if you need to search across multiple models, some of the code below will need additional configuration)

We need to determine what fields are going to be searchable. For a Job, those fields are “title”, “company”, and “location”. We’ll create a Phoenix migration to index those fields, using Postgres’ GIN indexes – a specific type of index designed to speed up full-text search. Read more in the Postgres documentation, if you’re interested.

defmodule Workwithelixir.Repo.Migrations.AddIndexesToJob do
  use Ecto.Migration

  def change do
    execute("CREATE INDEX index_jobs_on_title ON jobs USING gin(to_tsvector('english', title))")
    execute("CREATE INDEX index_jobs_on_company ON jobs USING gin(to_tsvector('english', company))")
    execute("CREATE INDEX index_jobs_on_location ON jobs USING gin(to_tsvector('english', location))")
  end
end

With the fields indexed, we’ll create a Postgres “view”, which will create a singular access point to searching all the fields on our model:

defmodule Workwithelixir.Repo.Migrations.AddSearchesView do
  use Ecto.Migration

  def change do
    execute("
      CREATE VIEW searches AS

      SELECT
        jobs.id AS searchable_id,
        'Job' AS searchable_type,
        jobs.title AS term
      FROM jobs

      UNION

      SELECT
        jobs.id AS searchable_id,
        'Job' AS searchable_type,
        jobs.company AS term
      FROM jobs

      UNION

      SELECT
        jobs.id AS searchable_id,
        'Job' AS searchable_type,
        jobs.location AS term
      FROM jobs
    ")
  end
end

(Note here that if you are using multiple models, you should also UNION here with a SELECT from each model… the pattern for each field should be pretty straightforward)

With this view created, we can test the query in psql:

workwithelixir_dev=# select * from searches;
-[ RECORD 1 ]---+------------------
searchable_id   | 2
searchable_type | Job
term            | Backend Engineer
-[ RECORD 2 ]---+------------------
searchable_id   | 2
searchable_type | Job
term            | Acme Co
-[ RECORD 3 ]---+------------------
searchable_id   | 2
searchable_type | Job
term            | Los Angeles, CA

As we can see, the searches view formats each field on a model as an individual record to be searched on. Each of these fields returns a searchable_id and searchable_type, which we can use to look up the model itself.

Let’s add a search query. To do this, we’ll make use of the to_ts* (text search) utilities Postgres provides, for transforming an input string and matching it against the text that we’re searching. Note that at any point in the code, you can test out the Postgres to_tsvector/to_tsquery functions in the psql interface – we’ll breeze through them a bit as the Postgres documentation covers them pretty well.

workwithelixir_dev=# select * from searches where to_tsvector('english', term) @@ to_tsquery('english', 'acme');
-[ RECORD 1 ]---+--------
searchable_id   | 2
searchable_type | Job
term            | Acme Co

In this instance, we can see that passing in the query acme has matched against the Job record with the term “Acme Co”. One thing I realized here is this view doesn’t include the column that is matched. In this case, this record has matched our query with a term on the company field, but we have no way of knowing that. I’d encourage you, if you need that information, to revise the “searches” view we implemented above.

One more thing – if you test the above SQL with the query “acme co”, you’ll notice it fails. This is because Postgres text search queries have interesting behavior around spaces – instead of “acme co”, we need to provide either “acme&co” (match a term with “acme” AND “co”) or “acme|co” (match a term with “acme” OR “co”). You can determine what kind of search you’d like – in my application’s case, I decided to use |, so I could do searches like “acme|los|angeles|engineer” and match multiple terms at once.

At this point, we can begin adding code in our Phoenix project to allow searching directly in the web application.

We need to do two things in our application: first, add code to execute and load models from SQL, and using that code, execute something like the above SQL to receive a set of models based on a query.

I elected to use a solution like the one in this StackOverflow post, in lib/<yourapp>/repo.ex:

defmodule Workwithelixir.Repo do
  # ...

  def execute_and_load(sql, params, model) do
    Ecto.Adapters.SQL.query!(__MODULE__, sql, params)
    |> load_into(model)
  end

  defp load_into(response, model) do
    Enum.map(response.rows, fn row ->
      fields = Enum.reduce(Enum.zip(response.columns, row), %{}, fn({key, value}, map) ->
        Map.put(map, key, value)
      end)
      Ecto.Schema.__load__(model, nil, nil, nil, fields,
                           &Ecto.Type.adapter_load(__adapter__, &1, &2))
    end)
  end
end

With that implemented, we can add a search/1 function, which accepts a query and returns a set of Jobs matching that term:

defmodule Workwithelixir.Job do
  # ...

  @doc """
  Search for jobs based on a provided term, using Postgres full-text search.

  Returns [Job]
  """
  def search(term) do
    formatted = term |> String.replace(" ", "|")
    Repo.execute_and_load("select * from jobs where id in (select searchable_id from searches where to_tsvector('english', term) @@ to_tsquery($1));", [ formatted ], Job)
  end
end

The above code is pretty straightforward – first, we take the input term and transform it to use | instead of spaces (remember “acme|co” versus “acme&co”. We then use pretty similar SQL to what we used in the console – we search using the “searches” view for a term, select the searchable_id for all of the returned results, and then, using those ids, look up the actual Job rows using their ids. The execute_and_load function accepts a SQL statement, any variables, and finally, a model to load the data in. This is an Ecto thing that I can best explain as making a strongly-formatted Map of your model fields (like %Job{title: nil, company: nil, location: nil}), and then taking the SQL output to fill out that map.

With that, we can boot up a iex console and make sure that we get the data we expect:

$ iex -S mix

iex(2)> Job.search("backend")
[debug] QUERY OK db=32.4ms
select * from jobs where id in (select searchable_id from searches where to_tsvector('english', term) @@ to_tsquery($1)); ["backend"]
[%Workwithelixir.Job{__meta__: #Ecto.Schema.Metadata<:loaded, "jobs">,
  id: 2, title: "Backend Engineer" ...>}]

What a neat little way to build powerful text search! I’ll leave the remainder of setting this up, UI-wise, as an exercise to the reader. It could be as simple (as it is in my case) as a controller function that accepts query as a param and passes it to this function.

This guide was built with Elixir 1.2 and Phoenix 1.2.1 – if you’re on a previous (or later) versions of Elixir, Phoenix, or Ecto, this may not work as expected – sorry about that.

Bytesized offers technical training for companies. [Contact us][contact] to schedule Elixir and Phoenix training for your team today.

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”.