Feeds

Author

Jul 03 2019
Jul 03

The information that follows is not meant to diagnose or treat specific medical issues regarding the hands but rather is a list of habits that have kept me healthy and able to continue doing the things I love with either greatly reduced or no pain. I am not a doctor. Proceed with caution and common sense.

I grew up playing video games. Many, many hours were spent leveling up, collecting every collectible, wringing every last hour I could out of each game I got ahold of. When I got to high school, I started getting interested in music. First was the drums, then piano, and finally the guitar. My first career was as a guitar teacher. I would play guitar and teach throughout the day, then unwind by playing video games in the evening. Then I started learning to build websites and write code. I eventually shifted careers into web development, got back to playing guitar strictly for fun, and continued to play video games when I made the time. This is when things started to fall apart.

With the exception of time spent walking, running, or sleeping, I was using my hands all day long. Even while watching television, I’d have my guitar in my hands running exercises over and over. Without giving myself regular breaks, my hands began hurting and it became difficult to continue doing the things I wanted and needed to do in my life. I saw a hand specialist who suggested I either wear braces all the time or stop doing certain activities. Neither of these was really an option. I needed to figure out how to continue to do the things I enjoyed doing without hurting myself.

Below are 4 habits I’ve formed in the years since my pain was at its worst that have allowed me to work a full-time computer job, practice guitar daily for upwards of two hours, and play video games as long as I’d like with little or no pain.

Habit #1: Stretch

I’ll start with the habit that has brought me the most benefit, which is stretching. I begin every single day, even non-working days, by going through a specific series of stretches that takes about 5 minutes to do. After seeing that doctor, buying books on hand problems, reading many articles, and wearing hand braces, I stumbled upon a video on YouTube from a man who’d had even more serious hand pain than I did. His short video details his experience and demonstrates the series of stretches that were recommended to him by a physical therapist. To his series of stretches, I’ve added a standing side bend because it just feels good.

I can’t overly stress just how important this routine has been to my hand health. I strongly suggest you give it a go.

Habit #2: Move

The advent of activity trackers and the proliferation of standing desks reveals that keeping your body moving throughout the day is becoming a priority for many people. My doctor suggested I stop doing the activities that were causing me pain. Instead, I’ve made an effort to do the activities that were causing me pain for shorter stretches of time. If you’re working at a computer for 8 hours a day, that’s fine so long as you take breaks frequently and change things up just as often.

For me this means using my laptop with an external monitor at my desk, both sitting and standing, or sitting on a cushion on the floor with the laptop on a low stool, or using the laptop on the kitchen table. Variety is an important part of this, not the individual tools. I personally don’t like external keyboards and mice. The way my Mac is laid out is the most comfortable for me. But I can still become fatigued if I stay long enough in the same position. My watch reminds me to move each hour if I haven’t. A calendar alert or Pomodoro timer would work just as well. Don’t get too mired in the numbers or any kind of “system” if you don’t need to. Just move!

Habit #3: Move More

Habit #2 is all about changing up how you’re working throughout the day, but it’s important to take breaks from working entirely to give your hands a break and your body some exercise. This can take many forms. My go-tos are taking walks and meditating.

Walking is just plain wonderful. It gets all your limbs moving, it’s basically stress-free on your body, gets the heart rate up, and gets your mind working. I can’t count the number of times I’ve been banging my head against a problem for hours only to have the answer pop into my head once I was out walking. There’s something about stepping away from a problem for a while that allows one’s mind to process it more clearly. Even when I don’t come up with the actual solution to a problem, I invariably come up with at least something new to try.

Meditating is also just plain wonderful. Have a seat, close your eyes, focus your attention on your breathing, and try to relax tense parts of your face and body. Unlike walking, the goal here isn’t necessarily to have eureka moments. Quite the opposite; it’s to allow your mind to rest, improve your patience and focus, and allow you to think more clearly when you need to. The topic of meditation is worthy of an article of its own, but it’s easy enough to try it out now. You’ll be surprised at how much more productive (and happier) you can be after just a few minutes of “doing nothing.”

Habit #4: Strengthen

If you went through the stretches detailed in the video above you may have noticed that many of them stretch large muscles in the forearms, arms, and shoulders rather than the small muscles of the wrists and hands. You can think of the body as being structured much like a pyramid. Our large bones and muscles support successively smaller ones as our limbs extend out from the torso. When we perform fine motions with our fingers and hands repeatedly, we put undue stress on muscles that aren’t strong enough to handle them. The solution to this problem is to strengthen the larger muscles that support our hands going up our arms to provide a sturdier base for these more intricate motions.

Include exercises in your weekly routine that target your upper body, like pull-ups, push-ups, and kettlebell swings. It isn’t important to be able to do large numbers of repetitions, especially if you’re new to upper body workouts. Focus instead on getting the motion right and not doing too much. A little strength training goes a long way and for our purposes we’re looking only to build muscle, not to become a bodybuilder or fundamentally change our overall body composition.

Conclusion

Serious hand problems are serious and you should seek the help of a professional hand doctor and/or physical therapist to diagnose and treat your specific issue. But if you haven’t quite developed a serious condition and want to practice habits that will allow you to live a life free of hand pain, I highly recommend you give the ideas above a try. They cost only your time and time spent working to prevent injury and pain is always time well spent.

Feb 27 2019
Feb 27

Introduction

Anyone who’s built a React app of any appreciable complexity knows how challenging it can be selecting and configuring the multitude of libraries you’ll need to make it fast and performant. Gatsby, a static-site generator built with React and GraphQL, alleviates these pain points while also providing a straightforward way to consume data from an API. On the back-end, we can leverage Drupal’s content modeling, creation, and editing tools along with the JSON:API module to serve that content to our Gatsby front-end. Together, Gatsby and Drupal form a powerful combination that makes an excellent case for decoupling your next project.

What You’ll Build and Learn

In this article, we’ll take a hands-on look at using Drupal, the JSON:API module, and Gatsby to build a simple blog. We’ll create an index page listing all of our blog posts with a brief snippet of the article, and a page for each post with the full text. By the end, you should feel comfortable pulling JSON into a Gatsby site and displaying it on the page.

  • Drupal 8 - Drupal 8 will be used locally to model and manage our content. We’ll take advantage of the article content type that ships with the standard profile and the convenient admin interface for content creation.
  • JSON:API - This Drupal 8 module will take care of exporting our content in well-formed JSON.
  • Gatsby - We’ll use Gatsby to build our front-end and consume content from Drupal.

Prerequisites

  • Familiarity with creating and navigating a fresh install of Drupal 8, including creating content and adding/enabling modules
  • NPM and Node 10+ installed
  • Basic understanding of React

Step 1: Drupal 8 + JSON:API

Let’s begin with a fresh install of Drupal 8.

We’ll create three articles so we’ll have some stuff to work with.

Time to install the JSON:API module. Head over to https://www.drupal.org/project/jsonapi and copy a link to the module download. Once installed, enable both JSON:API and the Serialization modules and you’re done. We now automatically have endpoints created and available for our two content types. Let’s hit one via Postman to verify this using this URL:

http://localhost:8888/gatsbyblogdrupal/jsonapi/node/article

As the URL indicates, we’re accessing the JSON output for nodes of type article.

We can see a list of all the articles returned. If we wanted to retrieve a specific article, we just need to append the node id to the end of the URL:

http://localhost:8888/gatsbyblogdrupal/jsonapi/node/article/e2e62d88-7c5f-443d-9008-0dc5d79e1391

And with that, we’re ready to move on to Gatsby.

Step 2: Gatsby

From your command line, navigate to a directory where you’d like to create your application. From here, install the Gatsby CLI by typing npm install --global gatsby-cli. You can find more detailed installation instructions at https://www.gatsbyjs.org/tutorial/part-zero/ if you need them.

Run gatsby new gatsby_blog to create the app, change into the new directory that’s generated, then run gatsby develop to start the dev server. Hitting http://localhost:8000 shows you the starter page and confirms you’re up and running properly:

Let’s take a quick tour of Gatsby.

  1. Open your project directory in your editor of choice and navigate to src/pages. You’ll see a file each for the index page (which you just saw above), an example of a second page, and a 404 page.
  2. Taking a look at index.js, you’ll see a <Layout> component wrapping some markup that you see on the index page.
  3. Make a change to the <h1> text here to see your site live reload.
  4. Near the bottom, there’s a <Link> component that points us to the second page.

You may notice that the big purple header you see in the browser isn’t shown in the index file. That bit is part of the <Layout> component. Navigate to src/components and open layout.js.

  1. About midway down the page, you’ll see a <Header> component with a siteTitle prop. It points to data.site.siteMetadata.title.
  2. Just above that, you’ll see a <StaticQuery> component, which appears to be doing something related to that site title. This is just a taste of how Gatsby makes use of GraphQL to manage app data. We’ll explore this more fully in the next section of this article. For now, take note that we’re accessing siteMetadata to fetch the site’s title.

Head over to gatsby-config.js to see where siteMetadata is set.

  1. Right at the top of gatsby-config.js we can see a big JS object is being exported. The first property in this object is siteMetadata, and within that, we can see the title property. Change the title property to “My Gatsby Blog” and save. Your site will hot reload and that purple header’s text will change to match the config.

The plan is to have our index show the latest blog posts in reverse chronological order like a standard Drupal site would. We’ll show the post headline as a link, its image if it has one, and a short piece of the article. Clicking on the headline will take us to a full page view of the post. It will be a pretty barebones blog but should illustrate nicely how Gatsby and Drupal can work together.

Step 3: Gatsby with Real Data from Drupal

Gatsby uses GraphQL to pull in data from external sources and query for bits of it to use in your markup. Let’s take a look at how it works. When you ran gatsby develop earlier, you may have noticed a few lines that mention GraphQL:

It says we can visit http://localhost:8000/_graphql to explore our site’s data and schema using the GraphiQL IDE. Let’s hit that URL and see what’s up.

On the left-hand side, we can write queries that auto-complete and see the result of the query on the right-hand side. I like to come to this interface, experiment with queries, then copy and paste that query right into my app. To see how this works, we’ll write a query to fetch the site’s title.

  1. Start by typing an opening curly brace. This will automatically create the closing brace.
  2. From here, type ctrl + space to bring up the auto-complete. This gives you a list of all the possible properties available to query. Typing anything will also bring up the auto-complete.
  3. Type the word “site”, then another set of curly braces. Ctrl + space again will show the auto-complete with a much smaller list of options. Here you’ll see siteMetadata.
  4. Type siteMetadata (or use auto-complete), followed by another set of curly braces.
  5. Type ctrl + space one more time to bring up the auto-complete, where we’ll see title as an option.
  6. Type “title” and then ctrl + enter (or the play button at the top) to run the query. On the right-hand side, we’ll see the result.

And there we have our site title. Take a look back to src/components/layout.js. You’ll see this exact query (with a little formatting) as the query prop of the <StaticQuery> component at the top. This is the approach we’ll use to build our queries when we start pulling in data from Drupal.

If you have any experience with React apps and pulling in data, any method to which you’re already accustomed will still work inside a Gatsby app (it’s still React, after all). You can use AJAX, the fetch API, async/await, or any third-party library you like. However, when working with Drupal (or a number of other common data sources), there’s a simpler approach: source plugins. Head over to https://www.gatsbyjs.org/packages/gatsby-source-drupal/ for details about this plugin, which is designed to automatically pull data from Drupal via JSON:API.

From within your project directory, run npm install --save gatsby-source-drupal. This will make the package available to use in our config. Open gatsby-config.js and notice the property called plugins. There’s already one plugin defined: gatsby-source-filesystem. This is what’s allowing the app to access local files via GraphQL, like that astronaut image we saw on the index page earlier. We’ll follow a similar pattern to add the Drupal source plugin just below this one:

{
  resolve: `gatsby-source-drupal`,
  options: {
    baseUrl: `http://localhost:8888/gatsby_blog_drupal/`,
  },
},

We’ve created a new object inside the plugins array with two properties: resolve and options. We set resolve to gatsby-source-drupal (note the back-ticks instead of quotes) and set the baseUrl option to the path of our Drupal site. Now, restart the Gatsby server. You should see something like this scroll by in the command line during server startup:

So, what’s happened? With the Drupal source plugin installed and configured, on startup Gatsby will fetch all the data available from Drupal, observe any GraphQL queries you’ve made inside your components, and apply the data. Pertinent to the site we’re building, it’ll make allNodeArticle available to query, which represents an array of all the article nodes on our site. Head back to your GraphiQL IDE (http://localhost:8000/_graphql) and check it out. Here’s what a query for the titles of all articles looks like:

Step 4: Querying Drupal from Gatsby

Now data from our Drupal site is available to our Gatsby app. We just need to insert some queries into our code. As of the release of Gatsby 2.0, there are two different ways of querying data with GraphQL. Let’s take a look at both options before deciding what’s best suited for our site.

Page Level Query

First is the page-level query. This method can only be used when the component represents a full page like our index.js, page-2.js, and 404.js files. For the page-level query, at the bottom of index.js, create a new variable called query that contains the full GraphQL query we looked at just a moment ago.

export const query = graphql`
  query {
    allNodeArticle {
      edges {
        node {
          title
        }
      }
    }
  }
`

Remember to import { graphql } from "gatsby" at the top of the file. Next, we need to make the data returned from this query available to our page component. Inside the parenthesis for our <IndexPage> component, add the data variable like so:

const IndexPage = ({ data }) => ( ...

The data variable is now available for use in our component. Note that we’re putting it between curly braces, which is ES6 shorthand for creating an object whose key is the same as the variable name. Let’s output the title of our first blog post just to confirm everything’s working. Add the following to the markup of the index page component:

<p>{ data.allNodeArticle.edges[0].node.title }</p>

You should see the title of the blog post added to the page of your running Gatsby site. Unpacking this a bit:

  1. reach into the data variable for the array containing all articles
  2. each item in this array is considered an “edge” — a term originating from Relay
  3. grab the first post at index 0
  4. access its node property
  5. and finally its title

You just utilized real data from Drupal in your Gatsby app!

If there’s a case where you have a smaller, non-page-level component into which you’d like to pull data, there’s another method for doing so, called Static Query.

Static Queries

A Static Query is a higher-order component that accepts two props: a query, and a render method. To accomplish the same outcome as our previous example, you’d wipe-out everything currently inside your index page component and replace it with this:

const IndexPage = () => (
  <StaticQuery
    query={graphql`
      query {
        allNodeArticle {
          edges {
            node {
              title
            }
          }
        }
      }
    `}
    render={data => (
      <Layout>
        <SEO title="Home" keywords={[`gatsby`, `application`, `react`]} />
        <p>{ data.allNodeArticle.edges[0].node.title }</p>
        <h1>Hi people</h1>
        <p>Welcome to your new Gatsby site.</p>
        <p>Now go build something great.</p>
        <div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}>
          <Image />
        </div>
        <Link to="/page-2/">Go to page 2</Link>
      </Layout>
    )}
  />
)

Remember to import { graphql, StaticQuery } from "gatsby" at the top of the file. Your page should reload automatically and show the same output as before. Using Static Query allows for more flexibility in how you pull in data. For the rest of this article, I’ll be using the first method since we only need to query data on the page level. Stay tuned to this page for details about the differences between the two options.

Step 5: Building Out Our Pages

Time to start putting our Drupal data to use for real. We’ll start with the index page, where we want our blog listing.

First, we need to update our page query to also pull in a few more things we’ll need, including the body of each blog post, its created date, and its image:

allNodeArticle {
  edges {
    node {
      title
      body {
        value
      }
      created
      relationships {
        field_image {
          localFile {
            childImageSharp {
              fluid(maxWidth: 400, quality: 100) {
                ...GatsbyImageSharpFluid
              }
            }
          }
        }
      }
    }
  }
},

Working with images in Gatsby is an article all its own. The high-level explanation of all that stuff up there is:

  • field_image is filed under relationships, along with things like tags
  • Gatsby processes all images referenced in your site files, creating multiple versions of them to ensure the best quality to file size ratio at various screen sizes, which means we’re querying for a local file
  • childImageSharp is the result of some image transformation plugins that come configured by default, it packages up all the various quality/size options mentioned above
  • fluid represents a srcset of images to accommodate various screen sizes, more details here
  • …GatsbyImageSharpFluid is a query fragment, which comes from GraphQL world, more details here

Keep an eye on https://www.gatsbyjs.org/packages/gatsby-image/ for the latest documentation on using images in Gatsby. It’s a somewhat complicated subject, but if you play around in the GraphiQL IDE, you’ll find what you need. The end result is your images are transformed into various sizes to fit various breakpoints and are lazy-loaded with an attractive blur-up effect. As you’ll see below, we can use the <Img> component to insert these images into our page.

We’re now ready to map over all of our blog posts and spit out some markup:

{data.allNodeArticle.edges.map(edge => (
  <>
    <h3><Link to={ edge.node.id }>{ edge.node.title }</Link></h3>
    <small><em>{ Date(edge.node.created) }</em></small>
    <div style={{ maxWidth: `300px`, marginBottom: `1.45rem`, width: `100%` }}>
      <Img fluid={ edge.node.relationships.field_image.localFile.childImageSharp.fluid } />
    </div>
    <div dangerouslySetInnerHTML={{ __html: edge.node.body.value.split(' ').splice(0, 50).join(' ') + '...' }}></div>
  </>
))}

There’s a lot going on here, so let’s unpack it line by line:

  • line 1: in typical React fashion, we map over the individual article nodes
  • line 2: this is a React fragment, using shorthand syntax, it allows us to wrap several lines of markup without unnecessarily polluting it with wrapping divs
  • lines 3 & 4: here we access some values on each node
  • lines 5-7: here is our article image, wrapped in a div with CSS-in-JS styling to restrict overall width, then our <Img> component as mentioned above, more details on styling in Gatsby here
  • line 8: JSON:API provides the body content of our article as a string of HTML, so we can use React’s dangerouslySetInnerHTML element to convert the string to markup, preserving whatever formatting we included when creating our content

Additionally, I’ve converted the created date to a JavaScript Date object using the Date() function and manipulated the article body content to be truncated.

Nothing fancy, but it’s a good start. You may have noticed in the previous code snippet that our headline is linking to the article’s node id. Right now, that link is broken until we create pages for each node. Thankfully we don’t have to do this manually. Let’s take a look at how we can generate some pages automatically for each of our posts.

Head on over to gatsby-node.js and add the following:

exports.createPages = ({ graphql, actions }) => {
  return graphql(`
    {
     allNodeArticle {
       edges {
         node {
           id
         }
       }
     }
    }
  `
  ).then(result => {
   console.log(JSON.stringify(result, null, 4))
  })
}

Here we’re writing our own implementation of the createPages API. Gatsby will use this to generate pages for each node id. Stop and restart the development server to see the following output in your console:

"data": {
    "allNodeArticle": {
        "edges": [
            {
                "node": {
                    "id": "e2e62d88-7c5f-443d-9008-0dc5d79e1391"
                }
            },
            {
                "node": {
                    "id": "1058ebcb-c910-4127-a496-b808740e49a5"
                }
            },
            {
                "node": {
                    "id": "29326c97-c2bc-4161-bcd7-f1c4564343f2"
                }
            }
        ]
    }
}

Next, we’ll need a template for our posts. Create a file called src/templates/blog-post.js and paste in the following:

import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"
import Img from 'gatsby-image'

export default ({ data }) => {
  const post = data.nodeArticle
  return (
    <Layout>
      <div>
        <h1>{ post.title }</h1>
        <small><em>{ Date(post.created) }</em></small>
        <div style={{ maxWidth: `900px`, marginBottom: `1.45rem`, width: `100%` }}>
          <Img fluid={ post.relationships.field_image.localFile.childImageSharp.fluid } />
        </div>
        <div dangerouslySetInnerHTML={{ __html: post.body.value }}></div>
      </div>
    </Layout>
  )
}

export const query = graphql`
  query($id: String!) {
    nodeArticle(id: { eq: $id }) {
      title
      body {
        value
      }
      created
      relationships {
        field_image {
          localFile {
            childImageSharp {
              fluid(maxWidth: 400, quality: 100) {
                ...GatsbyImageSharpFluid
              }
            }
          }
        }
      }
    }
  }
`

Notice that we’re querying for a single article node now, passing in an id as an argument. Otherwise, it looks a lot like our index page. You’ll see how this template gets access to that id variable in the next snippet. With a template in place, we can update gatsby-node.js to use it when generating pages:

const path = require(`path`)

exports.createPages = ({ graphql, actions }) => {
  const { createPage } = actions
  return graphql(`
    {
     allNodeArticle {
       edges {
         node {
           id
         }
       }
     }
    }
  `
  ).then(result => {
    result.data.allNodeArticle.edges.forEach(({ node }) => {
      createPage({
        path: node.id,
        component: path.resolve(`./src/templates/blog-post.js`),
        context: {
          id: node.id,
        },
      })
    })
  })
}

We’re now passing each node’s id as a context variable to our template, which enables us to query that specific node and access its fields. Now the headline links in our index will link to a full page view of the post. Give it a try:

There’s plenty left we could do to enhance the site, like adding some of our own styling finding a friendlier date format, adding alt text to images, using slugs in place of ids in URLs, etc. But as of now, we have a functioning blog powered by data directly from Drupal. The best part is, we don’t have to worry at all about setting up a server, installing Drupal, and managing that whole side of things.

Step 6: Creating A Build and Deploying to GitHub Pages

In this final step, we’re ready to generate a build of the site we’ve developed and deployed it for free to GitHub Pages. Additionally, we’ll look at how we can streamline the process so updating our deployed site when new content is added is fast and easy.

We’ll start with a couple more Gatsby commands:

  • gatsby build - this creates a build of your site, complete with static files inside the public directory
  • gatsby serve - this allows you to preview your built site at http://localhost:9000/

If you haven’t yet, run git init in your project directory to initialize a new repo. To make deployment to GitHub Pages a snap, we’ll take advantage of a handy package called gh-pages. Run npm install --save-dev gh-pages to install, then we’ll add a script to our package.json file:

"scripts": {
  ...,
  "deploy": "gatsby build --prefix-paths && gh-pages -d public"
},

The site will be located inside a directory named for your repo. That --prefix-paths bit allows us to drill down into this directory. We just need to include the proper prefix path in our gatsby-config.js:

module.exports = {
  ...
  pathPrefix: "/gatsby_blog",
}

Head on over to GitHub and create yourself a new repository called gatsby_blog. Back on your command line, run git remote add origin http://github.com/username/gatsby_blog.git, substituting your own GitHub username.

Once that’s done, run npm run deploy and wait for the build and deploy to complete. If everything went correctly, you should be able to visit your live blog at http://username.github.io/gatsby_blog/. Congrats!

When you need to update your site’s content, simply run your deployment script again.

Conclusion

React is a powerful and flexible library for building dynamic websites, but it’s often only one of many libraries you’ll end up using to reach your goal. Gatsby gathers up everything you’ll likely need and handles all the tricky wiring for you, vastly improving the speed and quality of your site as well as the overall developer experience. Pairing it with Drupal’s excellent content management tools and the zero-config JSON:API module makes many aspects of decoupling your site much easier. This hybrid approach may be just what you need for your next project.

Further Reading

May 04 2018
May 04

Functional programming is all the rage, and for good reason. By introducing type systems, immutable values, and enforcing purity in our functions, to name just a few advantages, we can reduce the complexity of our code while bolstering our confidence that it will run with minimal errors. It was only a matter of time before these concepts crept their way into the increasingly sophisticated front-end technologies that power the web.

Projects like ClojureScript, Reason, and Elm seek to fulfill the promise of a more-functional web by allowing us to write our applications with functional programming restraints that compile down to regular ol’ JavaScript for use in the browser. Learning a new syntax and having to rely on a less-mature package ecosystem, however, are a couple roadblocks for many who might be interested in using compile-to-JS languages. Fortunately, great strides have been made in creating libraries to introduce powerful functional programming tenets directly into JavaScript codebases with a gentler learning curve.

One such library is Redux, which is a state-management tool heavily inspired by the aforementioned Elm programming language. Redux allows you to create a single store that holds the state of your entire app, rather than managing that state at the component level. This store is globally-available, allowing you to access the pieces of it that you need in whichever components need them without worrying about the shape of your component tree. The process of updating the store involves passing the store object and a descriptive string, called an action, into a special function called a reducer. This function then creates and returns a new store object with the changes described by the action.

This process is very reliable. We can be sure that the store will be updated in exactly the same way every single time so long as we pass the same action to the reducer. This predictable nature is critical in functional programming. But there’s a problem: what if we want our action to fire-off an API call? We can’t be sure what that call will return or that it’ll even succeed. This is known as a side effect and it’s a big no-no in the FP world. Thankfully, there’s a nice solution for managing these side effects in a predictable way: Redux-Saga. In this article, we’ll take a deeper look at the various problems one might run into while building their Redux-powered app and how Redux-Saga can help mitigate them.

Prerequisites

In this article, we’ll be building an application to store a list of monthly bills. We’ll focus specifically on the part that handles fetching the bills from a remote server. The pattern we’ll look at works just the same with POST requests. We’ll bootstrap this app with create-react-app, which will cover most of the code I don’t explicitly walkthrough.

What is Redux-Saga?

Redux-Saga is a Redux middleware, which means it has access to your app’s store and can dispatch its own actions. Similar to regular reducers, sagas are functions that listen for dispatched actions. Additionally, they perform side effects and return their own actions back to a normal reducer.

Redux Flow with Saga Middleware

By intercepting actions that cause side effects and handling them in their own way, we maintain the purity of Redux reducers. This implementation uses JS generators, which allows us to write asynchronous code that reads like synchronous code. We don’t need to worry about callbacks or race conditions since the generator function will automatically pause on each yield statement until complete before continuing. This improves the overall readability of our code. Let’s take a look at what a saga for loading bills from an API would look like.

 1   import { put, call, takeLatest } from 'redux-saga/effects';
 2   
 3   export function callAPI(method = 'GET', body) {
 4     const options = {
 5       headers,
 6       method
 7     }
 8   
 9     if (body !== undefined) {
10       options.body = body;
11     }
12   
13     return fetch(apiEndpoint, options)
14             .then(res => res.json())
15             .catch(err => { throw new Error(err.statusText) });
16   }
17   
18   export function* loadBills() {
19     try {
20       const bills = yield call(callAPI);
21       yield put({ type: 'LOAD_BILLS_SUCCESS', payload: bills });
22     } catch (error) {
23       yield put({ type: 'LOAD_BILLS_FAILURE', payload: error });
24     }
25   }
26   
27   export function* loadBillsSaga() {
28     yield takeLatest('LOAD_BILLS', loadBills);
29   }

Let’s tackle it line-by-line:

  • Line 1: We import several methods from redux-saga/effects. We’ll use takeLatest to listen for the action that kicks-off our fetch operation, call to perform said fetch operation, and put to fire the action back to our reducer upon either success or failure.
  • Line 3-16: We’ve got a helper function that handles the calls to the server using the fetch API.
  • Line 18: Here, we’re using a generator function, as denoted by the asterisk next to the function keyword.
  • Line 19: Inside, we’re using a try/catch to first try the API call and catch if there’s an error. This generator function will run until it encounters the first yield statement, then it will pause execution and yield out a value.
  • Line 20: Our first yield is our API call, which, appropriately, uses the call method. Though this is an asynchronous operation, since we’re using the yield keyword, we effectively wait until it’s complete before moving on.
  • Line 21: Once it’s done, we move on to the next yield, which makes use of the put method to send a new action to our reducer. Its type describes it as a successful fetch and contains a payload of the data fetched.
  • Line 23: If there’s an error with our API call, we’ll hit the catch block and instead fire a failure action. Whatever happens, we’ve ended up kicking the ball back to our reducer with plain JS objects. This is what allows us to maintain purity in our Redux reducer. Our reducer doesn't get involved with side effects. It continues to care only about simple JS objects describing state changes.
  • Line 27: Another generator function, which includes the takeLatest method. This method will listen for our LOAD_BILLS action and call our loadBills() function. If the LOAD_BILLS action fires again before the first operation completed, the first one will be canceled and replaced with the new one. If you don’t require this canceling behavior, redux-saga/effects offer the takeEvery method.

One way to look at this is that saga functions are a sort-of intercepting reducer for certain actions. We fire-off the LOAD_BILLS action, Redux-Saga intercepts that action (which would normally go straight to our reducer), our API call is made and either succeeds or fails, and finally, we dispatch an action to our reducer that handles the app’s state update. Oh, but how is Redux-Saga able to intercept Redux action calls? Let’s take a look at index.js to find out.

 1   import React from 'react';
 2   import ReactDOM from 'react-dom';
 3   import App from './App';
 4   import registerServiceWorker from './registerServiceWorker';
 5   import { Provider } from 'react-redux';
 6   import { createStore, applyMiddleware } from 'redux';
 7   import billsReducer from './reducers';
 8   
 9   import createSagaMiddleware from 'redux-saga';
10   import { loadBillsSaga } from './loadBillsSaga';
11   
12   const sagaMiddleware = createSagaMiddleware();
13   const store = createStore(
14     billsReducer,
15     applyMiddleware(sagaMiddleware)
16   );
17   
18   sagaMiddleware.run(loadBillsSaga);
19   
20   ReactDOM.render(
21     <Provider store={store}>
22       <App />
23     </Provider>,
24     document.getElementById('root')
25   );
26   registerServiceWorker();

The majority of this code is standard React/Redux stuff. Let’s go over what’s unique to Redux-Saga.

  • Line 6: Import applyMiddleware from redux. This will allow us to declare that actions should be intercepted by our sagas before being sent to our reducers.
  • Line 9: createSagaMiddleware from Redux-Saga will allow us to run our sagas.
  • Line 12: Create the middleware.
  • Line 15: Make use of Redux’s applyMiddleware to hook our saga middleware into the Redux store.
  • Line 18: Initialize the saga we imported. Remember that sagas are generator functions, which need to be called once before values can be yielded from them.

At this point, our sagas are running, meaning they’re waiting to respond to dispatched actions just like our reducers are. Which brings us to the last piece of the puzzle: we have to actually fire off the LOAD_BILLS action! Here’s the BillsList component:

 1   import React, { Component } from 'react';
 2   import Bill from './Bill';
 3   import { connect } from 'react-redux';
 4   
 5   class BillsList extends Component {
 6     componentDidMount() {
 7       this.props.dispatch({ type: 'LOAD_BILLS' });
 8     }
 9   
10     render() {
11       return (
12         <div className="BillsList">
13           {this.props.bills.length && this.props.bills.map((bill, i) =>
14             <Bill key={`bill-${i}`} bill={bill} />
15           )}
16         </div>
17       );
18     }
19   }
20   
21   const mapStateToProps = state => ({
22     bills: state.bills,
23     error: state.error
24   });
25   
26   export default connect(mapStateToProps)(BillsList);

I want to attempt to load the bills from the server once the BillsList component has mounted. Inside componentDidMount we fire off LOAD_BILLS using the dispatch method from Redux. We don’t need to import that method since it’s automatically available on all connected components. And this completes our example! Let’s break down the steps:

  1. BillsList component mounts, dispatching the LOAD_BILLS action
  2. loadBillsSaga responds to this action, calls loadBills
  3. loadBills calls the API to fetch the bills
  4. If successful, loadBills dispatches the LOAD_BILLS_SUCCESS action
  5. billsReducer responds to this action, updates the store
  6. Once the store is updated, BillsList re-renders with the list of bills

Testing

A nice benefit of using Redux-Saga and generator functions is that our async code becomes less-complicated to test. We don’t need to worry about mocking API services since all we care about are the action objects that our sagas output. Let’s take a look at some tests for our loadBills saga:

 1   import { put, call } from 'redux-saga/effects';
 2   import { callAPI, loadBills } from './loadBillsSaga';
 3   
 4   describe('loadBills saga tests', () => {
 5     const gen = loadBills();
 6     
 7     it('should call the API', () => {
 8       expect(gen.next().value).toEqual(call(callAPI));
 9     });
10   
11     it('should dispatch a LOAD_BILLS_SUCCESS action if successful', () => {
12       const bills = [
13         {
14           id: 0,
15           amountDue: 1000,
16           autoPay: false,
17           dateDue: 1,
18           description: "Bill 0",
19           payee: "Payee 0",
20           paid: true
21         },
22         {
23           id: 1,
24           amountDue: 1001,
25           autoPay: true,
26           dateDue: 2,
27           description: "Bill 1",
28           payee: "Payee 1",
29           paid: false
30         },
31         {
32           id: 2,
33           amountDue: 1002,
34           autoPay: false,
35           dateDue: 3,
36           description: "Bill 2",
37           payee: "Payee 2",
38           paid: true
39         }
40       ];
41       expect(gen.next(bills).value).toEqual(put({ type: 'LOAD_BILLS_SUCCESS', payload: bills }));
42     });
43   
44     it('should dispatch a LOAD_BILLS_FAILURE action if unsuccessful', () => {
45       expect(gen.throw({ error: 'Something went wrong!' }).value).toEqual(put({ type: 'LOAD_BILLS_FAILURE', payload: { error: 'Something went wrong!' } }));
46     });
47   
48     it('should be done', () => {
49       expect(gen.next().done).toEqual(true);
50     });
51   });

Here we’re making use of Jest, which create-react-app provides and configures for us. This makes things like describe, it, and expect available without any importing required. Taking a look at what this saga is doing, I’ve identified 4 things I’d like to test:

  • The saga fires off the request to the server
  • If the request succeeds, a success action with a payload of an array of bills is returned
  • If the request fails, a failure action with a payload of an error is returned
  • The saga returns a done status when complete

By leveraging the put and call methods from Redux-Saga, I don’t need to worry about mocking the API. The call method does not actually execute the function, rather it describes what we want to happen. This should seem familiar since it’s exactly what Redux does. Redux actions don’t actually do anything themselves. They’re just JavaScript objects describing the change. Redux-Saga operates on this same idea, which makes testing more straightforward. We just want to assert that the API was called and that we got the appropriate Redux action back, along with any expected payload.

  • Line 5: first we need to initialize the saga (aka run the generator function). Once it’s running we can start to yield values out of it. The first test, then, is simple.
  • Line 8: call the next method of the generator and access its value. Since we used the call method from Redux-Saga instead of calling the API directly, this will look something like this:
{ '@@redux-saga/IO': true, CALL: { context: null, fn: [Function: callAPI], args: [] } }

This is telling us that we’re planning to fire-off the callAPI function as we described in our saga. We then compare this to passing callAPI directly into the call method and we should get the same descriptor object each time.

  • Line 11: Next we want to test that, given a successful response from the API, we return a new action with a payload of the bills we retrieved. Remember that this action will then be sent to our Redux reducer to handle updating the app state.
  • Line 12-40: Start by creating some dummy bills we can pass into our generator.
  • Line 41: Perform the assertion. Again we call the next method of our generator, but this time we pass-in the bills array we created. This means that when our generator reaches the next yield keyword, this argument will be available to it. We then compare the value after calling next to a call using the put method from Redux-Saga with the action.
  • Line 44-46: When testing the failure case, instead of plainly calling the next method on our generator, we instead use the throw method, passing in an error message. This will cause the saga to enter its catch block, where we expect to find an action with the error message as its payload. Thus, we make that assertion.
  • Line 48-50: Finally, we want to test that we’ve covered all the yield statements by asserting that the generator has no values left to return. When a generator has done its job, it will return an object with a done property set to true. If that’s the case, our tests for this saga are complete!

Conclusion

We’ve achieved several objectively useful things by incorporating Redux-Saga into our project:

  • Our async code has a more synchronous look to it thanks to the use of generators
  • Our Redux reducers remain pure (no side effects)
  • Our async code is simpler to test

I hope this article has given you enough information to understand how Redux-Saga works and what problems it solves, and made a case for why you should consider using it.

Further Reading

Header photo by Becky Matsubara

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web