{
    "componentChunkName": "component---src-templates-post-js",
    "path": "/blog/2016/03/20/build-an-image-gallery-using-redux-saga",
    "result": {"data":{"site":{"siteMetadata":{"title":"your friend Joel's digital garden","description":"Articles and notes from a collaborator at egghead.io. Musings on software, business, and life from a skilled virtual assistant.","author":{"name":"Joel Hooks"},"keywords":["Video Blogger"]}},"mdx":{"excerpt":"Building an Image Gallery The image gallery we will build is a simple application that displays an array of image URLs loaded from a service (Flickr), and allows the user to select them individually. It will be built with React, using Redux…","fields":{"github":"https://github.com/joelhooks/joelhooks-com/tree/master/content/legacy_blog/2016-03-20-build-an-image-gallery-using-redux-saga.markdown"},"body":"var _excluded = [\"components\"];\n\nfunction _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"layout\": \"post\",\n  \"title\": \"Build an Image Gallery Using React, Redux and redux-saga\",\n  \"date\": \"2016-03-20T00:00:00.000Z\"\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, _excluded);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h1\", null, \"Building an Image Gallery\"), mdx(\"p\", null, \"The image gallery we will build is a simple application that displays an array of image URLs loaded from a service (Flickr), and allows the user to select them individually.\"), mdx(\"p\", null, mdx(\"img\", {\n    parentName: \"p\",\n    \"src\": \"https://s3.amazonaws.com/f.cl.ly/items/3v0l00410J1Z1j310b24/Screen%20Shot%202016-03-20%20at%203.42.17%20PM.png?v=1b32daca\",\n    \"alt\": null\n  })), mdx(\"p\", null, \"It will be built with React, using Redux and redux-saga. React is being used as the core framework to take advantage of its virtual-dom implementation. Redux will handle the management of state within the application. Finally, we will use redux-saga to handle the complexity of asynchronous sequences.\"), mdx(\"p\", null, \"We will write the gallery in ES6 (arrow functions, modules, and template strings!), so we will need to do a bit of project setup to get going.\"), mdx(\"h2\", null, \"Project Setup and Automation\"), mdx(\"p\", null, \"There is a significant array of options when it comes to getting started with a React application. For this simple app, we want to keep it as minimal as possible. We are going to use Babel to transpile ES6 into good ol\\u2019 ES5 for the browser, budo/browserify to serve it locally, and tape to test.\"), mdx(\"p\", null, \"Create a file called package.json in a new project folder and add the following contents to it:\"), mdx(\"h3\", null, \"package.json\"), mdx(\"p\", null, \"{\\n\\\"name\\\": \\\"egghead-react-redux-image-gallery\\\",\\n\\\"version\\\": \\\"0.0.1\\\",\\n\\\"description\\\": \\\"Redux Saga beginner tutorial\\\",\\n\\\"main\\\": \\\"src/main.js\\\",\\n\\\"scripts\\\": {\\n\\\"test\\\": \\\"babel-node ./src/saga.spec.js | tap-spec\\\",\\n\\\"start\\\": \\\"budo ./src/main.js:build.js --dir ./src --verbose --live -- -t babelify\\\"\\n},\\n\\\"repository\\\": {\\n\\\"type\\\": \\\"git\\\",\\n\\\"url\\\": \\\"git+\", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/joelhooks/egghead-react-redux-image-gallery.git%22\"\n  }, \"https://github.com/joelhooks/egghead-react-redux-image-gallery.git\\\"\"), \"\\n},\\n\\\"author\\\": \\\"Joel Hooks \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"mailto:joelhooks@gmail.com\"\n  }, \"joelhooks@gmail.com\"), \"\\\",\\n\\\"license\\\": \\\"MIT\\\",\\n\\\"dependencies\\\": {\\n\\\"babel-polyfill\\\": \\\"6.3.14\\\",\\n\\\"react\\\": \\\"^0.14.3\\\",\\n\\\"react-dom\\\": \\\"^0.14.3\\\",\\n\\\"react-redux\\\": \\\"^4.4.1\\\",\\n\\\"redux\\\": \\\"^3.3.1\\\",\\n\\\"redux-saga\\\": \\\"^0.8.0\\\"\\n},\\n\\\"devDependencies\\\": {\\n\\\"babel-cli\\\": \\\"^6.1.18\\\",\\n\\\"babel-core\\\": \\\"6.4.0\\\",\\n\\\"babel-preset-es2015\\\": \\\"^6.1.18\\\",\\n\\\"babel-preset-react\\\": \\\"^6.1.18\\\",\\n\\\"babel-preset-stage-2\\\": \\\"^6.1.18\\\",\\n\\\"babelify\\\": \\\"^7.2.0\\\",\\n\\\"browserify\\\": \\\"^13.0.0\\\",\\n\\\"budo\\\": \\\"^8.0.4\\\",\\n\\\"tap-spec\\\": \\\"^4.1.1\\\",\\n\\\"tape\\\": \\\"^4.2.2\\\"\\n}\\n}\"), mdx(\"p\", null, \"With the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"package.json\"), \" in place, you can run \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"npm install\"), \" in the project folder and install all of the dependencies that we will need.\"), mdx(\"p\", null, \"We're also going to need to configure Babel with a .babelrc file in the project folder that contains the Babel presets that we want to use:\"), mdx(\"h3\", null, \".babelrc\"), mdx(\"p\", null, \"{\\n\\\"presets\\\": \", \"[\\\"es2015\\\", \\\"react\\\", \\\"stage-2\\\"]\", \"\\n}\"), mdx(\"p\", null, \"This file tells babel that we will be using ES2015 (ES6), React, and stage-2 features of the emerging ECMAScript standard (ES2016).\"), mdx(\"p\", null, \"The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"package.json\"), \" has two standard scripts configured called \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"start\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"test\"), \". For right now, we want to get \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"start\"), \" working so we can load the application. The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"start\"), \" script is currently configured to look inside of a folder called \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"src\"), \" so \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"create a folder called \", mdx(\"inlineCode\", {\n    parentName: \"em\"\n  }, \"src\")), \" in the project directory and add the following files:\"), mdx(\"h3\", null, \"index.html\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-html\"\n  }, \"<!doctype html>\\n<html>\\n<head>\\n  <meta charset=\\\"utf-8\\\">\\n  <meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1.0\\\">\\n  <title>egghead: React Redux Image Gallery</title>\\n  <link rel=\\\"stylesheet\\\" href=\\\"styles.css\\\">\\n</head>\\n<body>\\n<div class=\\\"title\\\">\\n  <img src=\\\"https://cloud.egghead.io/2G021h3t2K10/download/egghead-logo-head-only.svg\\\" class=\\\"egghead\\\">\\n  <h3>Egghead Image Gallery</h3>\\n</div>\\n\\n<div id=\\\"root\\\"></div>\\n\\n<script type=\\\"text/javascript\\\" src=\\\"build.js\\\"></script>\\n</body>\\n</html>\\n\")), mdx(\"h3\", null, \"main.js\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"import 'babel-polyfill';\\n\\nimport React from 'react';\\nimport ReactDOM from 'react-dom';\\n\\nReactDOM.render(\\n  <h1>Hello React!</h1>,\\n  document.getElementById('root'),\\n);\\n\")), mdx(\"h3\", null, \"styles.css\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-css\"\n  }, \"body {\\n  font-family: Helvetica, Arial, Sans-Serif, sans-serif;\\n  background: white;\\n}\\n\\n.title {\\n  display: flex;\\n  padding: 2px;\\n}\\n\\n.egghead {\\n  width: 30px;\\n  padding: 5px;\\n}\\n\\n.image-gallery {\\n  width: 300px;\\n  display: flex;\\n  flex-direction: column;\\n  border: 1px solid darkgray;\\n}\\n\\n.gallery-image {\\n  height: 250px;\\n  display: flex;\\n  align-items: center;\\n  justify-content: center;\\n}\\n\\n.gallery-image img {\\n  width: 100%;\\n  max-height: 250px;\\n}\\n\\n.image-scroller {\\n  display: flex;\\n  justify-content: space-around;\\n  overflow: auto;\\n  overflow-y: hidden;\\n}\\n\\n.image-scroller img {\\n  width: 50px;\\n  height: 50px;\\n  padding: 1px;\\n  border: 1px solid black;\\n}\\n\")), mdx(\"p\", null, \"The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"index.html\"), \" loads the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"styles.css\"), \" to give us some basic styling/layout. It also loads the script \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"build.js\"), \", which is a \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"generated\"), \" file. Our \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"main.js\"), \" is a very basic React application that renders an \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"h1\"), \" into the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"#root\"), \" element inside of \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"index.html\"), \". With these files in place, you should now be able to run \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"strong\"\n  }, \"npm start\")), \" in the project folder and navigate to \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://10.11.12.1:9966/\"\n  }, \"https://10.11.12.1:9966/\"), \" and see the following:\"), mdx(\"p\", null, mdx(\"img\", {\n    parentName: \"p\",\n    \"src\": \"https://s3.amazonaws.com/f.cl.ly/items/2I1V0o2c1d281f3i3408/Screen%20Shot%202016-03-20%20at%204.31.06%20PM.png?v=1720e99d\",\n    \"alt\": null\n  })), mdx(\"p\", null, \"Now we will build the base \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Gallery\"), \" React component.\"), mdx(\"h2\", null, \"Displaying some images in the gallery.\"), mdx(\"p\", null, \"First thing's first, and we want to get some images displayed as quickly as possible! We will create a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Gallery\"), \" component that will display our images. In the project folder, create a file called \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Gallery.js\"), \" with the following contents.\"), mdx(\"h3\", null, \"Gallery.js\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"import React, { Component } from 'react';\\n\\nconst flickrImages = [\\n  'https://farm2.staticflickr.com/1553/25266806624_fdd55cecbc.jpg',\\n  'https://farm2.staticflickr.com/1581/25283151224_50f8da511e.jpg',\\n  'https://farm2.staticflickr.com/1653/25265109363_f204ea7b54.jpg',\\n  'https://farm2.staticflickr.com/1571/25911417225_a74c8041b0.jpg',\\n  'https://farm2.staticflickr.com/1450/25888412766_44745cbca3.jpg',\\n];\\n\\nexport default class Gallery extends Component {\\n  constructor(props) {\\n    super(props);\\n    this.state = {\\n      images: flickrImages,\\n      selectedImage: flickrImages[0],\\n    };\\n  }\\n  render() {\\n    const { images, selectedImage } = this.state;\\n    return (\\n      <div className=\\\"image-gallery\\\">\\n        <div className=\\\"gallery-image\\\">\\n          <div>\\n            <img src={selectedImage} />\\n          </div>\\n        </div>\\n        <div className=\\\"image-scroller\\\">\\n          {images.map((image, index) => (\\n            <div key={index}>\\n              <img src={image} />\\n            </div>\\n          ))}\\n        </div>\\n      </div>\\n    );\\n  }\\n}\\n\")), mdx(\"p\", null, \"We've hard coded an array of data into this component, which is a great way to start working quickly. The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Gallery\"), \" extends \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Component\"), \", and in its constructor we set the initial state of the component. Finally, we render a basic structure with some styled markup. The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"image-scroller\"), \" element uses the images array to produce multiple elements using \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"map\"), \".\"), mdx(\"p\", null, \"With the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Gallery\"), \" created, we can update \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"main.js\"), \" to load the gallery:\"), mdx(\"h3\", null, \"main.js\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"import \\\"babel-polyfill\\\"\\n\\nimport React from 'react'\\nimport ReactDOM from 'react-dom'\\n\\n+ import Gallery from './Gallery'\\n\\nReactDOM.render(\\n-  <h1>Hello React!</h1>,\\n+  <Gallery />,\\n  document.getElementById('root')\\n);\\n\")), mdx(\"p\", null, \"For now, we are using the hard-coded image URLs (via the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"flickrImages\"), \" array), and displaying the first image url as the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"selectedImage\"), \". We're accessing these properties by setting a default initial state within the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Gallery\"), \" component class constructor.\"), mdx(\"p\", null, mdx(\"img\", {\n    parentName: \"p\",\n    \"src\": \"https://s3.amazonaws.com/f.cl.ly/items/1T3G3p3T2q2H00472T1C/Screen%20Shot%202016-03-20%20at%204.51.14%20PM.png?v=afdd9bf7\",\n    \"alt\": null\n  })), mdx(\"p\", null, \"We can add interactivity to the gallery with an event handler that calls the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"setState\"), \" method on the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Gallery\"), \" component:\"), mdx(\"h3\", null, \"Gallery.js\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"export default class Gallery extends Component {\\n  constructor(props) {\\n    super(props);\\n    this.state = {\\n      images: flickrImages,\\n      selectedImage: flickrImages[0]\\n    }\\n  }\\n+  handleThumbClick(selectedImage) {\\n+    this.setState({\\n+      selectedImage\\n+   })\\n+  }\\n  render() {\\n    const {images, selectedImage} = this.state;\\n    return (\\n      <div className=\\\"image-gallery\\\">\\n        <div className=\\\"gallery-image\\\">\\n          <div>\\n            <img src={selectedImage} />\\n          </div>\\n        </div>\\n        <div className=\\\"image-scroller\\\">\\n          {images.map((image, index) => (\\n-            <div key={index}>\\n+            <div key={index} onClick={this.handleThumbClick.bind(this,image)}>\\n              <img src={image}/>\\n            </div>\\n          ))}\\n        </div>\\n      </div>\\n    )\\n  }\\n}\\n\")), mdx(\"p\", null, \"By adding \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"handleThumbClick\"), \" to the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Gallery\"), \" component class, we can access it in any elements \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"onClick\"), \" method. Note that we are using \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"bind(this,image)\"), \" in the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"onClick\"), \". By passing \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"image\"), \" as the second argument, it is sent as the first argument to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"handleThumbClick\"), \". This use of bind is an extermely handy way to pass \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"context\"), \" to an event handler.\"), mdx(\"p\", null, \"Looking good! Now we have some interaction, and something that resembles an \\\"app\\\". Now that we've dealt with getting the application running and displaying data, we can consider loading some remote data. The most obvious place to do that is one of the React component lifecycle methods. We will use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"componentDidMount\"), \" and make a call to the Flickr API and load some images:\"), mdx(\"h3\", null, \"Gallery.js\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"export default class Gallery extends Component {\\n  constructor(props) {\\n    super(props);\\n    this.state = {\\n      images: flickrImages,\\n      selectedImage: flickrImages[0]\\n    }\\n  }\\n+  componentDidMount() {\\n+    const API_KEY = 'a46a979f39c49975dbdd23b378e6d3d5';\\n+    const API_ENDPOINT = `https://api.flickr.com/services/rest/?method=flickr.interestingness.+getList&api_key=${API_KEY}&format=json&nojsoncallback=1&per_page=5`;+\\n+\\n+    fetch(API_ENDPOINT).then((response) => {\\n+      return response.json().then((json) => {\\n+        const images = json.photos.photo.map(({farm, server, id, secret}) => {\\n+            return `https://farm${farm}.staticflickr.com/${server}/${id}_${secret}.jpg`\\n+        });\\n+\\n+        this.setState({images, selectedImage: images[0]});\\n+      })\\n+    })\\n+  }\\n[...]\\n\")), mdx(\"p\", null, \"We've added a new method to the Gallery class. We are using React's \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"componentDidMount\"), \" lifecycle method to trigger the loading of data from Flickr. Lifecycle methods are called by React at specific times in a component's lifecycle. In this case, the method will be called whenever the component is added to the DOM. Note that the Gallery component is only added to the DOM once, so this will give us our initial load of images. For a more dynamic component that is loaded and unloaded over an application's lifecycle, this might cause excessive service calls or other unforeseen results.\"), mdx(\"p\", null, \"We are using the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"fetch\"), \" browser API to make a request to Flickr. Fetch returns a promise that resolves with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"response\"), \" object. Calling \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"response.json()\"), \" gives us another promise, which is the actual JSON result we are looking for. We'll map over the photos to create an array of Flickr image urls.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"Let's be honest. This application is simple. We could stop right here and we'd have the basic requirements done. Maybe we add an error handler in the fetch promise chain, some logic to see if there are images and \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"DONE!\"), \" At this point you really have to stop and use your imagination a bit. Simple requirements rarely last in the real world. Soon the application will grow as feature requests roll in. Authentication, a slide show, the ability to load different galleries and images sets... This \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"is not\"), \" good enough.\")), mdx(\"p\", null, \"So now that we have a working image gallery using React, we can start thinking about the foundation of patterns that we want to use to grow the application over time. The first order of business is to take away the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Gallery\"), \" component's power to control the state of the application.\"), mdx(\"p\", null, \"We are going to introduce Redux to manage state instead, so let's get that setup.\"), mdx(\"h2\", null, \"Using Redux to manage state\"), mdx(\"p\", null, \"Anytime you use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"setState\"), \" in your application \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"you've allowed the Component to become stateful\"), \". While this isn't always bad, it can lead to some confusing application code over time, with state management spread from top to bottom in your application.\"), mdx(\"p\", null, \"The Flux architecture is one solution that was introduced to help alleviate this. Flux moves logic and state into Stores. Stores are updated when Actions are dispatched in the application. Updates to Stores will trigger Views to be rendered with the new state.\"), mdx(\"p\", null, \"So why not just drop in Flux? It's \\\"official\\\" architecture after all.\"), mdx(\"p\", null, \"Well, \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Redux is basically Flux\"), \", but with some distinct advantages. Here's what \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://stackoverflow.com/a/32920459/87002\"\n  }, \"Dan Abramov (the creator of Redux) has to say\"), \":\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"Redux is not that different from Flux. Overall it's the same architecture, but Redux is able to cut some complexity corners by using functional composition where Flux uses callback registration.\")), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"It's not fundamentally different, but I find it that Redux makes certain abstractions easier, or at least possible to implement, that would be hard or impossible to implement in Flux.\")), mdx(\"p\", null, \"The \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://redux.js.org/\"\n  }, \"Redux documentation\"), \" is great. Dan is a truly gracious and inspiring individual. If you haven't \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://code-cartoons.com/a-cartoon-intro-to-redux-3afb775501a6#.7cra4lukv\"\n  }, \"read the code cartoons\"), \" or watched \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://egghead.io/series/getting-started-with-redux\"\n  }, \"Dan's free egghead.io series\"), \", now is the time!\"), mdx(\"h3\", null, \"Bootstrapping Redux\"), mdx(\"p\", null, \"The first thing we need to do is get Redux bootstrapped and running in our application. We don't have to install anything, that was all taken care of when we ran \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"npm install\"), \", but we do need to import and configure Redux.\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"The reducer function is the brain of Redux\"), \". When an action is dispatched by the application, the reducer receives the action and creates the piece of application state that the reducer owns. Since \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"reducers are pure functions\"), \", they can be composed together to create the complete state of the application. Let's create a simple reducer in the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"src\"), \" folder:\"), mdx(\"h3\", null, \"reducer.js\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"export default function images(state, action) {\\n  console.log(state, action);\\n  return state;\\n}\\n\")), mdx(\"p\", null, \"A reducer function is a function that takes two arguments.\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"state\"), \" - this is the data that represents the state of the application. The reducer function will use this state to construct the new state. If no state has changed as result of the action, the reducer will simply return the state input.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"action\"), \" - the event that has triggered the reducer. Actions are dispatched by the store, and handled by reducers. The action is required to have a \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"type\"), \" property that the reducer uses to apply changes to the new application state.\")), mdx(\"p\", null, \"For right now, the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"images\"), \" reducer will log to the console to prove that it is connected and ready for work. To use the reducer we need to configure redux in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"main.js\"), \":\"), mdx(\"h3\", null, \"main.js\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"import \\\"babel-polyfill\\\";\\n\\nimport React from 'react';\\nimport ReactDOM from 'react-dom';\\n\\nimport Gallery from './Gallery';\\n\\n+ import { createStore } from 'redux'\\n+ import reducer from './reducer'\\n\\n+ const store = createStore(reducer);\\n\\n+ import {Provider} from 'react-redux';\\n\\nReactDOM.render(\\n+  <Provider store={store}>\\n    <Gallery />\\n+  </Provider>,\\n  document.getElementById('root')\\n);\\n\")), mdx(\"p\", null, \"We are going to import the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"createStore\"), \" function from the Redux library. \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"createStore\"), \" is used to create the Redux store. For the most part, we don't interact directly with the store, it is something that Redux manages for us behind the scenes.\"), mdx(\"p\", null, \"We also need to import the reducer function that we've just created so that it can be delivered to the store.\"), mdx(\"p\", null, \"We will use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"createStore(reducer)\"), \" to configure the store with our application's reducer. This example only has a single reducer, but \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"createStore\"), \" can take multiple reducers arguments. More on that a bit later!\"), mdx(\"p\", null, \"Finally we import the higher-order \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Provider\"), \" component from \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"react-Redux\"), \". This will wrap our \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Gallery\"), \" so that we can make easy use of Redux. We need to pass the store we just created to the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Provider\"), \" so that it can use it for us. You could use Redux without \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Provider\"), \", and in fact, React isn't required to use Redux at all! That's wonderful, but we are going to use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Provider\"), \" because it is very convenient.\"), mdx(\"p\", null, \"If you open your developer tools console, you should see a message!\"), mdx(\"p\", null, mdx(\"img\", {\n    parentName: \"p\",\n    \"src\": \"https://s3.amazonaws.com/f.cl.ly/items/2R3b143J1Y3T400i2c0w/Screen%20Shot%202016-03-20%20at%206.32.23%20PM.png?v=ba488b8e\",\n    \"alt\": null\n  })), mdx(\"p\", null, \"It may be a bit mysterious, but shows off an interesting point of Redux. \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"All reducers receive all actions that are dispatched in the application.\"), \" In this case we are seeing an action that Redux itself dispatches.\"), mdx(\"h3\", null, \"Connecting the gallery component\"), mdx(\"p\", null, \"With Redux, we will use the concept of \\\"connected\\\" and \\\"un-connected\\\" components. A connected component is wired into the store, and coordinates and controls action events and the store. Usually a connected component will have children that are \\\"pure components\\\" that take data as input, and render when that data is updated. These children are unconnected components.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"note:\"), \" While React and Redux play very well together, React is \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"not\"), \" required for Redux. You can use Redux without React!\")), mdx(\"p\", null, \"react-redux provides a convenient wrapper for React components that does most of the heavy lifting required to connect a React component to a Redux store. We will add that to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Gallery\"), \" and make it our primary connected component:\"), mdx(\"h3\", null, \"Gallery.js\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"import React, {Component} from 'react'\\n+import {connect} from 'react-redux';\\n\\n-export default class Gallery extends Component {\\n+export class Gallery extends Component {\\n  constructor(props) {\\n    super(props);\\n+    console.log(props);\\n    this.state = {\\n      images: []\\n    }\\n  }\\n  componentDidMount() {\\n    const API_KEY = 'a46a979f39c49975dbdd23b378e6d3d5';\\n    const API_ENDPOINT = `https://api.flickr.com/services/rest/?method=flickr.interestingness.getList&api_key=${API_KEY}&format=json&nojsoncallback=1&per_page=5`;\\n\\n    fetch(API_ENDPOINT).then((response) => {\\n      return response.json().then((json) => {\\n        const images = json.photos.photo.map(({farm, server, id, secret}) => {\\n            return `https://farm${farm}.staticflickr.com/${server}/${id}_${secret}.jpg`\\n        });\\n\\n        this.setState({images, selectedImage: images[0]});\\n      })\\n    })\\n  }\\n  handleThumbClick(selectedImage) {\\n    this.setState({\\n      selectedImage\\n    })\\n  }\\n  render() {\\n    const {images, selectedImage} = this.state;\\n    return (\\n      <div className=\\\"image-gallery\\\">\\n        <div className=\\\"gallery-image\\\">\\n          <div>\\n            <img src={selectedImage} />\\n          </div>\\n        </div>\\n        <div className=\\\"image-scroller\\\">\\n          {images.map((image, index) => (\\n            <div key={index} onClick={this.handleThumbClick.bind(this,image)}>\\n              <img src={image}/>\\n            </div>\\n          ))}\\n        </div>\\n      </div>\\n    )\\n  }\\n}\\n\\n+export default connect()(Gallery)\\n\")), mdx(\"p\", null, \"Importing the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"connect\"), \" function from react-redux lets us export \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Gallery\"), \" by wrapping it in the connect component. Notice that \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"connect()(Gallery)\"), \" puts \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Gallery\"), \" in a second set of parentheses. This is because \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"connect()\"), \" returns a function that expects a React component as an argument. The call to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"connect()\"), \" configures that function. Soon we'll pass in arguments to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"connect\"), \" to configure it for our applications specific actions and state structure.\"), mdx(\"p\", null, \"We are also exporting the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"connect\"), \" call as the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"default\"), \" for this module. This is important! Now, when we \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"import Gallery\"), \" it will import the connected component and not the basic class.\"), mdx(\"p\", null, mdx(\"img\", {\n    parentName: \"p\",\n    \"src\": \"https://s3.amazonaws.com/f.cl.ly/items/2k1Y1h3g1b0l1m0w212R/Screen%20Shot%202016-03-20%20at%206.42.19%20PM.png?v=bc54291d\",\n    \"alt\": null\n  })), mdx(\"p\", null, \"If you look at the console log that we've added to the constructor, you will see that the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Gallery\"), \" component's properties now include a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"dispatch\"), \" function. This is part of what \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"connect\"), \" has modified for us, and gives us the ability to dispatch our own action objects to the applications reducers.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"export class Gallery extends Component {\\n  constructor(props) {\\n    super(props);\\n+    this.props.dispatch({type: 'TEST'});\\n    this.state = {\\n      images: []\\n    }\\n  }\\n[...]\\n\")), mdx(\"p\", null, \"We can test it out by calling dispatch in the constructor. You should see a log statement from our reducer in your developer console. We've dispatched our first action! \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"Actions are plain old javascript objects\"), \" that have a required \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"type\"), \" property. They can have any number of other properties as well to pass along to the reducer, but the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"type\"), \" property allows reducers to listen for specific actions that they are interested in.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"export default function images(state, action) {\\n-  console.log(state, action)\\n+  switch(action.type) {\\n+    case 'TEST':\\n+      console.log('THIS IS ONLY A TEST')\\n+  }\\n  return state;\\n}\\n\")), mdx(\"p\", null, \"Generally reducers use a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"switch\"), \" block to filter messages they are interested in. The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"switch\"), \" uses the action's type, and the reducer does its work when it gets an action that matches one of the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"case\"), \"s of the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"switch\"), \".\"), mdx(\"p\", null, \"Our application is now wired to receive actions. Now we need to connect it to the state provided by the Redux store.\"), mdx(\"h3\", null, \"Default application state\"), mdx(\"p\", null, \"Because we are using \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"connect\"), \", we don't need to interact with the Redux store directly. We are going to configure a default application state to provide the Redux store.\"), mdx(\"h3\", null, \"reducer.js\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"const defaultState = {\\n  images: []\\n}\\n\\nexport default function images(state = defaultState, action) {\\n  switch(action.type) {\\n    case 'TEST':\\n-      console.log('THIS IS ONLY A TEST')\\n+      console.log(state, action)\\n+      return state;\\n+    default:\\n+      return state;\\n  }\\n-  return state;\\n}\\n\")), mdx(\"p\", null, \"We've created a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"defaultState\"), \" object that has an empty array as its images property. We will set the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"state\"), \" to default in the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"images\"), \" function parameters. Now, if we log out the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"state\"), \" property in our test case, you'll see that it isn't undefined! The reducer \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"needs to return the current state\"), \" of the application. This is important! Right now, we aren't making any changes, so we can just return the state. Note that we added a default case. The reducer should \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"always\"), \" return an object representing the state.\"), mdx(\"p\", null, \"In the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Gallery\"), \" we can also connect the applications state by mapping it to properties:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"import React, {Component} from 'react'\\nimport {connect} from 'react-redux';\\n\\nexport class Gallery extends Component {\\n  constructor(props) {\\n    super(props);\\n    this.props.dispatch({type: 'TEST'});\\n+    console.log(props);\\n-    this.state = {\\n-      images: []\\n-    }\\n  }\\n-  componentDidMount() {\\n-    const API_KEY = 'a46a979f39c49975dbdd23b378e6d3d5';\\n-    const API_ENDPOINT = `https://api.flickr.com/services/rest/?method=flickr.interestingness.-getList&api_key=${API_KEY}&format=json&nojsoncallback=1&per_page=5`;-\\n-\\n-    fetch(API_ENDPOINT).then((response) => {\\n-      return response.json().then((json) => {\\n-        const images = json.photos.photo.map(({farm, server, id, secret}) => {\\n-            return `https://farm${farm}.staticflickr.com/${server}/${id}_${secret}.jpg`\\n-        });\\n-\\n-        this.setState({images, selectedImage: images[0]});\\n-      })\\n-    })\\n-  }\\n-  handleThumbClick(selectedImage) {\\n-    this.setState({\\n-      selectedImage\\n-    })\\n-  }\\n  render() {\\n-    const {images, selectedImage} = this.state;\\n+    const {images, selectedImage} = this.props;\\n    return (\\n      <div className=\\\"image-gallery\\\">\\n        <div className=\\\"gallery-image\\\">\\n          <div>\\n            <img src={selectedImage} />\\n          </div>\\n        </div>\\n        <div className=\\\"image-scroller\\\">\\n          {images.map((image, index) => (\\n-            <div key={index} onClick={this.handleThumbClick.bind(this,image)}>\\n+            <div key={index}>\\n              <img src={image}/>\\n            </div>\\n          ))}\\n        </div>\\n      </div>\\n    )\\n  }\\n}\\n\\n+function mapStateToProps(state) {\\n+  return {\\n+    images: state.images\\n+    selectedImage: state.selectedImage\\n+  }\\n+}\\n\\n-export default connect()(Gallery)\\n+export default connect(mapStateToProps)(Gallery)\\n\")), mdx(\"p\", null, \"We are going to remove all of the image loading and interaction in the connected component for now. If you look towards the bottom of \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Gallery\"), \" you will notice that we created a function called \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"mapStateToProps\"), \" that takes a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"state\"), \" argument and returns an object that puts \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"state.images\"), \" into a property called \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"images\"), \". \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"mapStateToProps\"), \" is then passed as an argument to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"connect\"), \".\"), mdx(\"p\", null, \"As the name suggests \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"mapStateToProps\"), \" is a function that takes the current state, and assigns it to properties of the component. If you \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"console.log(props)\"), \" in the constructor, you will see that we now have access to the images array that we set as the default state in our reducer!\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"const defaultState = {\\n-  images: []\\n+  images: [\\n+    \\\"https://farm2.staticflickr.com/1553/25266806624_fdd55cecbc.jpg\\\",\\n+    \\\"https://farm2.staticflickr.com/1581/25283151224_50f8da511e.jpg\\\",\\n+    \\\"https://farm2.staticflickr.com/1653/25265109363_f204ea7b54.jpg\\\",\\n+    \\\"https://farm2.staticflickr.com/1571/25911417225_a74c8041b0.jpg\\\",\\n+    \\\"https://farm2.staticflickr.com/1450/25888412766_44745cbca3.jpg\\\"\\n+  ],\\n+  selectedImage: \\\"https://farm2.staticflickr.com/1553/25266806624_fdd55cecbc.jpg\\\"\\n}\\n\\nexport default function images(state = defaultState, action) {\\n  switch(action.type) {\\n    case 'TEST':\\n      console.log(state, action)\\n      return state;\\n    default:\\n      return state;\\n  }\\n}\\n\")), mdx(\"p\", null, \"If you update the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"images\"), \" array in the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"defaultState\"), \" you should see some images reappear in the gallery! Now we need to get image selection wired back up with an action that is dispatched when the user clicks a thumbnail.\"), mdx(\"h3\", null, \"Updating the state\"), mdx(\"p\", null, \"So how do we update the state with a new selected image?\"), mdx(\"p\", null, \"We're going to configure the reducer to listen for an \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"IMAGE_SELECTED\"), \" action, and update the state with the action's payload.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"const defaultState = {\\n  images: [\\n    \\\"https://farm2.staticflickr.com/1553/25266806624_fdd55cecbc.jpg\\\",\\n    \\\"https://farm2.staticflickr.com/1581/25283151224_50f8da511e.jpg\\\",\\n    \\\"https://farm2.staticflickr.com/1653/25265109363_f204ea7b54.jpg\\\",\\n    \\\"https://farm2.staticflickr.com/1571/25911417225_a74c8041b0.jpg\\\",\\n    \\\"https://farm2.staticflickr.com/1450/25888412766_44745cbca3.jpg\\\"\\n  ],\\n  selectedImage: \\\"https://farm2.staticflickr.com/1553/25266806624_fdd55cecbc.jpg\\\"\\n}\\n\\nexport default function images(state = defaultState, action) {\\n  switch(action.type) {\\n-    case 'TEST':\\n    case 'IMAGE_SELECTED':\\n-      return state;\\n+      return {...state, selectedImage: action.image};\\n    default:\\n      return state;\\n  }\\n}\\n\")), mdx(\"p\", null, \"Now the reducer is ready to receive the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"IMAGE_SELECTED\"), \" action should it be dispatched! Inside of the case, we are returning a \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"new state object\"), \" by \\\"spreading\\\" the existing state and overwriting the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"selectedImage\"), \" property. Checkout more on the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"...state\"), \" object spread technique in \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://egghead.io/lessons/javascript-redux-avoiding-array-mutations-with-concat-slice-and-spread\"\n  }, \"this video\"), \". It's excellent.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"import React, {Component} from 'react'\\nimport {connect} from 'react-redux';\\n\\nexport class Gallery extends Component {\\n-  constructor(props) {\\n-    super(props);\\n-    this.props.dispatch({type: 'TEST'});\\n-    console.log(props);\\n-  }\\n  render() {\\n-    const {images, selectedImage} = this.props;\\n+    const {images, selectedImage, dispatch} = this.props;\\n\\n    return (\\n      <div className=\\\"image-gallery\\\">\\n        <div className=\\\"gallery-image\\\">\\n          <div>\\n            <img src={selectedImage} />\\n          </div>\\n        </div>\\n        <div className=\\\"image-scroller\\\">\\n          {images.map((image, index) => (\\n-            <div key={index}>\\n+            <div key={index} onClick={() => dispatch({type:'IMAGE_SELECTED', image})}>\\n              <img src={image}/>\\n            </div>\\n          ))}\\n        </div>\\n      </div>\\n    )\\n  }\\n}\\n\\nfunction mapStateToProps(state) {\\n  return {\\n    images: state.images,\\n    selectedImage: state.selectedImage\\n  }\\n}\\n\\nexport default connect(mapStateToProps)(Gallery)\\n\")), mdx(\"p\", null, \"In the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Gallery\"), \", we will use the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"dispatch\"), \" function in the component props by calling it \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"inside of the body\"), \" of the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"onClick\"), \" handler function. For now we are just writing it inline for convenience, but once we make that change, we can now click a thumbnail, and it will update the selected image via the reducer!\"), mdx(\"p\", null, \"Using dispatch can be convenient way to quickly create generic actions, but soon we will want to make reusable actions that are well named. To do this, we will make use of \\\"action creators\\\".\"), mdx(\"h3\", null, \"Action Creators\"), mdx(\"p\", null, \"Action creators are functions that return configured action objects. We will add our first action creator to an new file called \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"actions.js\"), \".\"), mdx(\"h3\", null, \"actions.js\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"export const IMAGE_SELECTED = 'IMAGE_SELECTED';\\n\\nexport function selectImage(image) {\\n  return {\\n    type: IMAGE_SELECTED,\\n    image,\\n  };\\n}\\n\")), mdx(\"p\", null, \"This could now be imported directly into any file that needed to create a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"selectImage\"), \" action! \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"selectImage\"), \" is a pure function that only returns data. It takes an image as an argument, and adds that to the action object it creates and returns.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"note:\"), \" We are returning a plain JavaScript object, but the second property \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"image\"), \" might be weird if you haven't encountered this style before. Basically, in ES6, if you pass a property to an object like this it expands to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"image: 'whatever value was held by image'\"), \" in the resulting object. Super handy.\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\"\n  }, \"import  * as GalleryActions from './actions.js';\\n[...]\\nonClick={() => dispatch(GalleryActions.selectImage(image))}\\n\")), mdx(\"p\", null, \"This isn't much nicer than just using \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"dispatch\"), \" though.\"), mdx(\"p\", null, \"Luckily for us, this pattern is so common, Redux provides a much nicer way to do this with the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"bindActionCreators\"), \" function.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"import React, {Component} from 'react'\\nimport {connect} from 'react-redux';\\n+ import {bindActionCreators} from 'redux';\\n\\n+ import  * as GalleryActions from './actions.js';\\n\\nexport class Gallery extends Component {\\n  constructor(props) {\\n    super(props);\\n    this.props.dispatch({type: 'TEST'});\\n    console.log(props);\\n  }\\n  handleThumbClick(selectedImage) {\\n    this.setState({\\n      selectedImage\\n    })\\n  }\\n  render() {\\n-    const {images, selectedImage, dispatch} = this.props;\\n+    const {images, selectedImage, selectImage} = this.props;\\n    return (\\n      <div className=\\\"image-gallery\\\">\\n        <div className=\\\"gallery-image\\\">\\n          <div>\\n            <img src={selectedImage} />\\n          </div>\\n        </div>\\n        <div className=\\\"image-scroller\\\">\\n          {images.map((image, index) => (\\n-            <div key={index} onClick={() => dispatch({type:'IMAGE_SELECTED', image})}>\\n+            <div key={index} onClick={() => selectImage(image)}>\\n              <img src={image}/>\\n            </div>\\n          ))}\\n        </div>\\n      </div>\\n    )\\n  }\\n}\\n\\nfunction mapStateToProps(state) {\\n  return {\\n    images: state.images,\\n    selectedImage: state.selectedImage\\n  }\\n}\\n\\n+function mapActionCreatorsToProps(dispatch) {\\n+  return bindActionCreators(GalleryActions, dispatch);\\n+}\\n\\n-export default connect(mapStateToProps)(Gallery)\\n+export default connect(mapStateToProps, mapActionCreatorsToProps)(Gallery)\\n\")), mdx(\"p\", null, \"We've added a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"mapActionCreatorsToProps\"), \" function that takes the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"dispatch\"), \" function as an argument. It returns the result of a call to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"bindActionCreators\"), \" with our \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"GalleryActions\"), \" provided as an argument. Now if you log the props, you'll see that \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Gallery\"), \" \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"no longer gets passed the \", mdx(\"inlineCode\", {\n    parentName: \"em\"\n  }, \"dispatch\"), \" function\"), \", and instead has a function called \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"selectImage\"), \" that we can use directly!\"), mdx(\"p\", null, \"To review, we've done several things:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"created a reducer that contains the initial (default) state of our application and listens for actions\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"created a store that consumes the reducer and provides a dispatcher that we can use to dispatch actions\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"connected our Gallery component to the store\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"mapped the store's state to props that are passed to the Gallery\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"mapped an action creator function so that the Gallery can simply call \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"selectImage(image)\"), \" and the application state will update.\")), mdx(\"p\", null, \"How do we use these patterns and load data from the remote data source?\"), mdx(\"p\", null, \"This is where it gets interesting! {% emoji wink %}\"), mdx(\"h2\", null, \"Asyncronous activity?\"), mdx(\"p\", null, \"You may hear the term \\\"side effects\\\" used when discussing a functional style of programming. Side effects are things that occur outside the boundaries of the application. Within our cozy bubble, side effects aren't really a problem, but when we reach out to a remote service the bubble is pierced. We lose some control, and we have to accept that fact.\"), mdx(\"p\", null, \"In Redux, \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"reducers don't have side effects\"), \". This means that \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"reducers don't handle async activity in our application\"), \". We can't use them to load our remote data because \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"reducers are pure functions with no side effects\"), \".\"), mdx(\"p\", null, \"Redux is wonderful, and if you don\\u2019t have and side-effects like asynchronous activity you could stop right here. If you\\u2019re creating more than the most trivial example, it is likely that you are loading data from a service, and this is of course async.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"note:\"), \" one of the coolest aspects of Redux is how tiny it is. It does so very little! It is intended to solve a very limited problem scope. Most applications will need to solve \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"lots\"), \" of problems! Luckily Redux provides the concept of \\\"middleware\\\", which are basically bits of code that sit in the midel of the action -> reducer -> store triangle and provide a mechanism for introducing side effects like async calls to remote servers\")), mdx(\"p\", null, \"One approach is to use \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, mdx(\"a\", {\n    parentName: \"strong\",\n    \"href\": \"https://en.wikipedia.org/wiki/Thunk\"\n  }, \"thunks\")), \" with the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/gaearon/redux-thunk\"\n  }, \"redux-thunk\"), \" middleware. Thunks work great, but can get confusing for sequences of actions and can be challenging to test effectively.\"), mdx(\"p\", null, \"Consider our image gallery application. When the application loads, it needs to:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"request an array of images from the server\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"display some notification that the images are loading\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"select an initial image for display when the results have been received\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"handle any errors that might occur\")), mdx(\"p\", null, \"This is all before the user has clicked anything in the application!\"), mdx(\"p\", null, \"So how do we do it?\"), mdx(\"p\", null, \"This is where \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"redux-saga\"), \" can be of great service to our application!\"), mdx(\"h2\", null, \"redux-saga\"), mdx(\"p\", null, \"redux-saga is built to handle asynchronous actions in our Redux applications. It provides middleware and a handful of effects methods that make building complex sequences of asynchronous actions a breeze.\"), mdx(\"p\", null, \"A saga is a \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"generator\"), \" function. Generators are an ES2015 addition to JavaScript. This might be your first encounter with generator functions, and if that\\u2019s the case, they might be a little weird. If that is the case, take a few minutes to read \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.2ality.com/2015/03/es6-generators.html\"\n  }, \"ES6 Generators in Depth\"), \" and watch this \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://egghead.io/lessons/ecmascript-6-generators\"\n  }, \"short generators video\"), \". Don\\u2019t fret to much if you\\u2019re still scratching your head. To use redux-saga you won\\u2019t need a PhD in JavaScript Async Programming. Promise.\"), mdx(\"p\", null, \"Because of the way generators work, we are able to create flat sequences of commands describing complex workflows within our application. The entire image loading sequence described above could look like this:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"export function* loadImages() {\\n  try {\\n    const images = yield call(fetchImages);\\n    yield put({ type: 'IMAGES_LOADED', images });\\n    yield put({ type: 'IMAGE_SELECTED', image: images[0] });\\n  } catch (error) {\\n    yield put({ type: 'IMAGE_LOAD_FAILURE', error });\\n  }\\n}\\n\\nexport function* watchForLoadImages() {\\n  while (true) {\\n    yield take('LOAD_IMAGES');\\n    yield call(loadImages);\\n  }\\n}\\n\")), mdx(\"h3\", null, \"The first saga\"), mdx(\"p\", null, \"We'll start with a simple example of a saga, and then we will configure redux-saga to connect it to our application. Create a file called \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"saga.js\"), \" in the source folder and add the following:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"export function* sayHello() {\\n  console.log('hello');\\n}\\n\")), mdx(\"p\", null, \"Our saga is simply a generator function. You can tell by the little \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"*\"), \" after \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"function*\"), \". It's called the \\\"super star\\\" {% emoji star %}!\"), mdx(\"p\", null, \"Now in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"main.js\"), \" we will import our new function and execute it.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"import \\\"babel-polyfill\\\";\\n\\nimport React from 'react';\\nimport ReactDOM from 'react-dom';\\n\\nimport Gallery from './Gallery';\\n\\nimport { createStore } from 'redux'\\nimport {Provider} from 'react-redux';\\nimport reducer from './reducer'\\n\\n+import {sayHello} from './sagas';\\n+sayHello();\\n\\nconst store = createStore(reducer);\\n\\nReactDOM.render(\\n  <Provider store={store}>\\n    <Gallery />\\n  </Provider>,\\n  document.getElementById('root')\\n);\\n\")), mdx(\"p\", null, \"No matter how long you stare at the console, your \\\"hello\\\" will never arrive {% emoji cry %}\"), mdx(\"p\", null, \"This is because \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"sayHello\"), \" is a \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"generator\"), \"! Generators don't execute immediately. If you changed the line to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"sayHello().next();\"), \" your greating will appear. Don't worry, we won't call \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"next\"), \" all the time. Like Redux, redux-saga is built to remove pain and bolierplate and make our development experience more pleasurable.\"), mdx(\"h2\", null, \"Configure redux-saga\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"import \\\"babel-polyfill\\\";\\n\\nimport React from 'react';\\nimport ReactDOM from 'react-dom';\\n\\nimport Gallery from './Gallery';\\n\\n-import { createStore } from 'redux'\\n+import { createStore, applyMiddleware } from 'redux'\\n+import createSagaMiddleware from 'redux-saga'\\nimport {Provider} from 'react-redux';\\nimport reducer from './reducer'\\n\\nimport {sayHello} from './sagas';\\n-sayHello()\\n\\n-const store = createStore(reducer);\\n+const store = createStore(\\n+  reducer,\\n+  applyMiddleware(createSagaMiddleware(sayHello))\\n+);\\n\\nReactDOM.render(\\n  <Provider store={store}>\\n    <Gallery />\\n  </Provider>,\\n  document.getElementById('root')\\n);\\n\")), mdx(\"p\", null, \"We've imported the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"applyMiddleware\"), \" function from Redux, and the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"createSagaMiddleware\"), \" from \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"redux-saga\"), \". When we create the store, we need to supply Redux with the middleware that we want to use. In this case we call \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"applyMiddleware\"), \" and send it the result of \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"createSagaMiddleware(sayHello)\"), \". Behind the scenes redux-saga loads in the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"sayHello\"), \" function, and politely calls the initial \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"next\"), \" for us.\"), mdx(\"p\", null, \"It should greet you in the console!\"), mdx(\"p\", null, \"Now let's build a saga for loading images.\"), mdx(\"h2\", null, \"Loading Images with a Saga\"), mdx(\"p\", null, \"We'ill get rid of the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"sayHello\"), \" saga and replace it with a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"loadImages\"), \" saga in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"sagas.js\"), \".\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"-export function* sayHello() {\\n-  console.log('hello');\\n-}\\n\\n+export function* loadImages() {\\n+  console.log('load some images please')\\n+}\\n\")), mdx(\"p\", null, \"We can't forget to update \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"main.js\"), \" as well:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"import \\\"babel-polyfill\\\";\\n\\nimport React from 'react';\\nimport ReactDOM from 'react-dom';\\n\\nimport Gallery from './Gallery';\\n\\nimport { createStore, applyMiddleware } from 'redux'\\nimport {Provider} from 'react-redux';\\nimport createSagaMiddleware from 'redux-saga'\\nimport reducer from './reducer'\\n\\n-import {sayHello} from './sagas';\\n+import {loadImages} from './sagas';\\n\\nconst store = createStore(\\n  reducer,\\n-  applyMiddleware(createSagaMiddleware(sayHello))\\n+  applyMiddleware(createSagaMiddleware(loadImages))\\n);\\n\\nReactDOM.render(\\n  <Provider store={store}>\\n    <Gallery />\\n  </Provider>,\\n  document.getElementById('root')\\n);\\n\")), mdx(\"p\", null, \"And now the saga is loading. Let's add our \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"fetchImages\"), \" method we used earlier inside of \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"sagas.js\"), \":\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"const API_KEY = 'a46a979f39c49975dbdd23b378e6d3d5';\\nconst API_ENDPOINT = `https://api.flickr.com/services/rest/?method=flickr.interestingness.getList&api_key=${API_KEY}&format=json&nojsoncallback=1&per_page=5`;\\n\\nconst fetchImages = () => {\\n  return fetch(API_ENDPOINT).then(function(response) {\\n    return response.json().then(function(json) {\\n      return json.photos.photo.map(\\n        ({ farm, server, id, secret }) =>\\n          `https://farm${farm}.staticflickr.com/${server}/${id}_${secret}.jpg`,\\n      );\\n    });\\n  });\\n};\\n\\nexport function* loadImages() {\\n  const images = yield fetchImages();\\n  console.log(images);\\n}\\n\")), mdx(\"p\", null, \"The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"fetchImages\"), \" method returns a promise. We are going to call \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"fetchImages\"), \", but we are going to use the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"yield\"), \" keyword. By dark arts and sorcery, generators \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"understand promises\"), \", and as the console log will show, we've yielded an array of image urls. Looking at \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"loadImages\"), \", it looks like typical syncronous code. The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"yield\"), \" keyword is the secret sauce that lets us code in this syncronous style for asyncronous activity.\"), mdx(\"h3\", null, \"Encapsulating our async API requests\"), mdx(\"p\", null, \"Let's define the api we want to use in its own file. It is nothing special. In fact, it's the same code we used earlier to load Flickr images. We're going to create a file called \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"flickr.js\"), \" in the src folder:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"const API_KEY = 'a46a979f39c49975dbdd23b378e6d3d5';\\nconst API_ENDPOINT = `https://api.flickr.com/services/rest/?method=flickr.interestingness.getList&api_key=${API_KEY}&format=json&nojsoncallback=1&per_page=5`;\\n\\nexport const fetchImages = () => {\\n  return fetch(API_ENDPOINT).then(function(response) {\\n    return response.json().then(function(json) {\\n      return json.photos.photo.map(\\n        ({ farm, server, id, secret }) =>\\n          `https://farm${farm}.staticflickr.com/${server}/${id}_${secret}.jpg`,\\n      );\\n    });\\n  });\\n};\\n\")), mdx(\"p\", null, \"This isn't strictly required, but it makes a lot of sense to me. We are at the very \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"boundaries of our application\"), \", where things are a bit messy. By encapsulating the mechanics of the interaction with the remote API, our code will be cleaner and easier to update. It also makes it dead simple to \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"completely swap out\"), \" the image service provider! Nice.\"), mdx(\"p\", null, \"Our \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"saga.js\"), \" should now look like this:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"import { fetchImages } from './flickr';\\n\\nexport function* loadImages() {\\n  const images = yield fetchImages();\\n  console.log(images);\\n}\\n\")), mdx(\"p\", null, \"We still need to get data out of our saga and into the application state. To handle this, we will utilize \\\"effects\\\" provided by redux-saga.\"), mdx(\"h3\", null, \"Update the application from a saga\"), mdx(\"p\", null, \"We could probably call our saga with the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"dispatch\"), \" function or store as an argument, but that approach would be unpleasant and perhaps a tad confusing over time. Instead, we'll rely on a method provided by redux-saga called \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"put\"), \".\"), mdx(\"p\", null, \"First we will update \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"reducer.js\"), \" to handle a new action type \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"IMAGES_LOADED\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"const defaultState = {\\n+  images: []\\n}\\n\\nexport default function images(state = defaultState, action) {\\n  switch(action.type) {\\n    case 'IMAGE_SELECTED':\\n      return {...state, selectedImage: action.image};\\n+    case 'IMAGES_LOADED':\\n+      return {...state, images: action.images};\\n    default:\\n      return state;\\n  }\\n}\\n\")), mdx(\"p\", null, \"We added the case, and also deleted the hard coded URLs from the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"defaultState\"), \". The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"IMAGES_LOADED\"), \" case now returns an updated state that includes images delivered by the action.\"), mdx(\"p\", null, \"Next we will update the saga:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"import {fetchImages} from './flickr';\\n+import {put} from 'redux-saga/effects';\\n\\nexport function* loadImages() {\\n  const images = yield fetchImages();\\n+  yield put({type: 'IMAGES_LOADED', images})\\n}\\n\")), mdx(\"p\", null, \"After importing \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"put\"), \", we add another line to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"loadImages\"), \". It \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"yield\"), \"s the result of the call the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"put\"), \" which sends along an action object. Behind the scenes redux-saga dispatches that for us, and the reducer receives the message!\"), mdx(\"p\", null, \"What if we don't want to loadImages implicitly like this, simply because we have wired up a saga? How do we trigger a saga with a specific type of action?\"), mdx(\"h3\", null, \"Triggering saga workflows with actions\"), mdx(\"p\", null, \"Sagas become much more useful if we have the ability to trigger their workflows with Redux actions. When we do this, we can leverage the power of sagas from any component in our app. First we will create a new saga called \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"watchForLoadImages\"), \".\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"import {fetchImages} from './flickr';\\n-import {put} from 'redux-saga/effects';\\n+import {put, take} from 'redux-saga/effects';\\n\\nexport function* loadImages() {\\n  const images = yield fetchImages();\\n  yield put({type: 'IMAGES_LOADED', images})\\n}\\n\\n+export function* watchForLoadImages() {\\n+  while(true) {\\n+    yield take('LOAD_IMAGES');\\n+    yield loadImages();\\n+  }\\n+}\\n\")), mdx(\"p\", null, \"This new saga uses a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"while\"), \" loop so that it is always active and ready. Inside of the loop we are yielding a call to a new method from redux-saga called \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"take\"), \". Take listens for actions of a given type, and when they occur, it advances the saga to the next yield. In this case, we are yielding a call to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"loadImages\"), \", which initiates the loading of images.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"import \\\"babel-polyfill\\\";\\n\\nimport React from 'react';\\nimport ReactDOM from 'react-dom';\\n\\nimport Gallery from './Gallery';\\n\\nimport { createStore, applyMiddleware } from 'redux'\\nimport {Provider} from 'react-redux';\\nimport createSagaMiddleware from 'redux-saga'\\nimport reducer from './reducer'\\n\\n-import {loadImages} from './sagas';\\n+import {loadImages} from './watchForLoadImages';\\n\\nconst store = createStore(\\n  reducer,\\n-  applyMiddleware(createSagaMiddleware(loadImages))\\n+  applyMiddleware(createSagaMiddleware(watchForLoadImages))\\n);\\n\\nReactDOM.render(\\n  <Provider store={store}>\\n    <Gallery />\\n  </Provider>,\\n  document.getElementById('root')\\n);\\n\")), mdx(\"p\", null, \"After updating \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"main.js\"), \", the application is no longer loading images. We can add a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"loadImages\"), \" action to our action creators.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"export const IMAGE_SELECTED = 'IMAGE_SELECTED';\\n+const LOAD_IMAGES = 'LOAD_IMAGES';\\n\\nexport function selectImage(image) {\\n  return {\\n    type: IMAGE_SELECTED,\\n    image\\n  }\\n}\\n\\n+export function loadImages() {\\n+  return {\\n+    type: LOAD_IMAGES\\n+  }\\n+}\\n\")), mdx(\"p\", null, \"Since we have already bound the action creators, all we need to do is call the action from the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Gallery\"), \" component.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"export class Gallery extends Component {\\n+  componentDidMount() {\\n+    this.props.loadImages();\\n+  }\\n  render() {\\n\")), mdx(\"h3\", null, \"Blocking and non-blocking effects\"), mdx(\"p\", null, \"This works fine for our application, but there is a broader issue that we should be concerned with. The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"watchForLoadImages\"), \" sage contains \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"blocking\"), \" effects. What does that mean? Well, it means that we can only execute a single \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"LOAD_IMAGES\"), \" workflow at a time! It isn't obvious with a simple example like this, because we actually only load images once, but it is definitely a consideration. In fact, the general practice when listening for action evens is to use the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"fork\"), \" effect instead of \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"yield loadImages()\"), \".\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"export function* watchForLoadImages() {\\n  while(true) {\\n    yield take('LOAD_IMAGES');\\n-    yield loadImages();\\n+    yield fork(loadImages); //be sure to import it!\\n  }\\n}\\n\")), mdx(\"p\", null, \"Using the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"fork\"), \" helper will convert our \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"watchForLoadImages\"), \" into a non-blocking saga that can be executed regardless of whether or not a previous call is in progress. redux-saga \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://yelouafi.github.io/redux-saga/docs/basics/UsingSagaHelpers.html\"\n  }, \"provides two helpers\"), \", \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"takeEvery\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"takeLatest\"), \" that assist in these situations.\"), mdx(\"h3\", null, \"Selecting the default image\"), mdx(\"p\", null, \"Sagas are sequences of actions, so we can add more aspects to a saga easily.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"import {fetchImages} from './flickr';\\nimport {put, take, fork} from 'redux-saga/effects';\\n\\nexport function* loadImages() {\\n  const images = yield fetchImages();\\n  yield put({type: 'IMAGES_LOADED', images})\\n+  yield put({type: 'IMAGE_SELECTED', image: images[0]})\\n}\\n\\nexport function* watchForLoadImages() {\\n  while(true) {\\n    yield take('LOAD_IMAGES');\\n    yield fork(loadImages);\\n  }\\n}\\n\")), mdx(\"p\", null, \"As part of the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"loadImages\"), \" workflow, we can yield another call to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"put\"), \" with the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"IMAGE_SELECTED\"), \" action type and send along the image we want to select when images are loaded.\"), mdx(\"h3\", null, \"Handling errors\"), mdx(\"p\", null, \"If something goes wrong inside of the saga, we might want to notify the application so that it can respond accordingly. Do do this, we simply wrap the workflow in a try/catch block, and yield a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"put\"), \" with a nitification that has the error as the payload.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\"\n  }, \"import {fetchImages} from './flickr';\\nimport {put, take, fork} from 'redux-saga/effects';\\n\\nexport function* loadImages() {\\n+  try {\\n    const images = yield fetchImages();\\n    yield put({type: 'IMAGES_LOADED', images})\\n    yield put({type: 'IMAGE_SELECTED', image: images[0]})\\n+  } catch(error) {\\n+    yield put({type: 'IMAGE_LOAD_FAILURE', error})\\n+  }\\n}\\n\\nexport function* watchForLoadImages() {\\n  while(true) {\\n    yield take('LOAD_IMAGES');\\n    yield fork(loadImages);\\n  }\\n}\\n\")), mdx(\"h2\", null, \"Testing Sagas\"), mdx(\"p\", null, \"Using Redux makes testing most of our app a breeze. Check out \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://egghead.io/series/react-testing-cookbook\"\n  }, \"this egghead course\"), \" for lots of techniques for testing React in general.\"), mdx(\"p\", null, \"One of the awesome aspects of redux-saga is how easy it makes testing these bits of asynchronous code. Testing async javascript can be a real chore. With sagas, we don't need to jump through hoops to test this core functionality of our application. Sagas take the pain out of aync tests! Which means we will write more tests. Right?\"), mdx(\"p\", null, \"We're going to use \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/substack/tape\"\n  }, \"tape\"), \". Let's set up a few tests for our saga.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\"\n  }, \"import test from 'tape';\\nimport {put, take} from 'redux-saga/effects'\\nimport {watchForLoadImages, loadImages} from './sagas';\\nimport {fetchImages} from './flickr';\\n\\ntest('watchForLoadImages', assert => {\\n  const generator = watchForLoadImages();\\n\\n  assert.end();\\n});\\n\")), mdx(\"p\", null, \"We'll go ahead and import everything we need for now, and add a single test. The test function takes a name and a function as arguments. Inside of the function, we create an instance of the sage generator. Armed with that instance, we can start advancing the saga to test each step.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"import test from 'tape';\\nimport {put, take} from 'redux-saga/effects'\\nimport {watchForLoadImages, loadImages} from './sagas';\\nimport {fetchImages} from './flickr';\\n\\ntest('watchForLoadImages', assert => {\\n  const generator = watchForLoadImages();\\n\\n+  assert.deepEqual(\\n+    generator.next().value,\\n+    false,\\n+    'watchForLoadImages should be waiting for LOAD_IMAGES action'\\n+  );\\n\\n  assert.end();\\n});\\n\")), mdx(\"p\", null, \"The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"assert.deepEqual\"), \" method takes two values and checks to see if they are equal (deeply!). The first is a call \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"generator.next().value\"), \" which advances the generator and gets the value. The next value is simple \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"false\"), \". We want to see it fail! The final argument is a description of the expected behavior.\"), mdx(\"p\", null, \"Run \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"npm test\"), \" in the project folder to see the result.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\"\n  }, \"\\u2716 watchLoadImages should be waiting for LOAD_IMAGES action\\n-----------------------------------------------------------\\n  operator: deepEqual\\n  expected: |-\\n    false\\n  actual: |-\\n    { TAKE: 'LOAD_IMAGES' }\\n\")), mdx(\"p\", null, \"The test fails as expected and the results are interesting. The actual result is \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"{ TAKE: 'LOAD_IMAGES' }\"), \", which is the output we receive when we call \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"take('LOAD_IMAGES');\"), \". In fact, our saga could yield an object instead of calling \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"take\"), \", but \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"take\"), \" gives us a little sugar and eliminates annoying keystrokes.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"import test from 'tape';\\nimport {put, take} from 'redux-saga/effects'\\nimport {watchForLoadImages, loadImages} from './sagas';\\nimport {fetchImages} from './flickr';\\n\\ntest('watchForLoadImages', assert => {\\n  const generator = watchForLoadImages();\\n\\n  assert.deepEqual(\\n    generator.next().value,\\n-    false\\n+    take('LOAD_IMAGES'),\\n    'watchForLoadImages should be waiting for LOAD_IMAGES action'\\n  );\\n\\n  assert.end();\\n});\\n\")), mdx(\"p\", null, \"We can simply call \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"take\"), \" in our test and get the result we need!\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"import test from 'tape';\\nimport {put, take} from 'redux-saga/effects'\\nimport {watchForLoadImages, loadImages} from './sagas';\\nimport {fetchImages} from './flickr';\\n\\ntest('watchForLoadImages', assert => {\\n  const generator = watchForLoadImages();\\n\\n  assert.deepEqual(\\n    generator.next().value,\\n    take('LOAD_IMAGES'),\\n    'watchForLoadImages should be waiting for LOAD_IMAGES action'\\n  );\\n\\n+  assert.deepEqual(\\n+    gen.next().value,\\n+    false,\\n+    'watchForLoadImages should call loadImages after LOAD_IMAGES action is received'\\n+  );\\n\\n  assert.end();\\n});\\n\")), mdx(\"p\", null, \"Our next test makes sure that the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"loadImages\"), \" saga is called as the next step in the workflow. We'll add a false value here to see the result.\"), mdx(\"p\", null, \"For a brief moment, let's update our saga code to yield the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"loadImages\"), \" saga:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"export function* watchForLoadImages() {\\n  while(true) {\\n    yield take('LOAD_IMAGES');\\n+    yield loadImages();\\n-    yield fork(loadImages); //be sure to import it!\\n  }\\n}\\n\")), mdx(\"p\", null, \"Now when you run the tests, you will see.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\"\n  }, \"\\u2716 watchForLoadImages should call loadImages after LOAD_IMAGES action is received\\n---------------------------------------------------------------------------------\\n  operator: deepEqual\\n  expected: |-\\n    false\\n  actual: |-\\n    { _invoke: [Function: invoke] }\\n\")), mdx(\"p\", null, \"Hmm, \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"{ _invoke: [Function: invoke] }\"), \" is \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"definitely\"), \" not as obvious as the simple object that we got when yeilding \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"take\"), \".\"), mdx(\"p\", null, \"This is a problem. Luckily it's one that redux-saga has solved in a nice way with effects like \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"fork\"), \". \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"take\"), \", \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"fork\"), \", and other effect methods return and easily testable simple object. The object is a \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"set of instructions\"), \" for redux-saga to execute. This is beautiful for testing because we don't have to worry about the actual side effects (like remote service calls). All we care about are the commands we are requesting to be executed.\"), mdx(\"p\", null, \"Let's update the saga to use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"fork\"), \" again:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"export function* watchForLoadImages() {\\n  while(true) {\\n    yield take('LOAD_IMAGES');\\n-    yield loadImages();\\n+    yield fork(loadImages);\\n  }\\n}\\n\")), mdx(\"p\", null, \"We will go back to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"yield fork(loadImages)\"), \" instead of yielding \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"loadImages\"), \" directly. Note that we aren't executing \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"loadImages\"), \". Instead we are \", \"*\", \"passing the function \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"loadImages\"), \" as an argument to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"fork\")), mdx(\"p\", null, \"Run \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"npm test\"), \" again:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\"\n  }, \"\\u2716 watchForLoadImages should call loadImages after LOAD_IMAGES action is received\\n---------------------------------------------------------------------------------\\n  operator: deepEqual\\n  expected: |-\\n    false\\n  actual: |-\\n    { FORK: { args: [], context: null, fn: [Function: loadImages] } }\\n\")), mdx(\"p\", null, \"Instead of a function invocation, we get a plain object. That object has the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"loadImages\"), \" function embedded in it. The application loads exactly the same in the browser, but now we can easily test this step in the saga workflow.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-diff\"\n  }, \"import test from 'tape';\\nimport {put, take} from 'redux-saga/effects'\\nimport {watchForLoadImages, loadImages} from './sagas';\\nimport {fetchImages} from './flickr';\\n\\ntest('watchForLoadImages', assert => {\\n  const generator = watchForLoadImages();\\n\\n  assert.deepEqual(\\n    generator.next().value,\\n    take('LOAD_IMAGES'),\\n    'watchForLoadImages should be waiting for LOAD_IMAGES action'\\n  );\\n\\n  assert.deepEqual(\\n    generator.next().value,\\n-    false,\\n+    yield fork(loadImages),\\n    'watchForLoadImages should call loadImages after LOAD_IMAGES action is received'\\n  );\\n\\n  assert.end();\\n});\\n\")), mdx(\"p\", null, \"Testing the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"loadImages\"), \" saga is similar. We need to update \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"yield fetchImages\"), \" to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"yield fork(fetchImages)\"), \".\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"test('loadImages', assert => {\\n  const gen = loadImages();\\n\\n  assert.deepEqual(\\n    gen.next().value,\\n    call(fetchImages),\\n    'loadImages should call the fetchImages api',\\n  );\\n\\n  const images = [0];\\n\\n  assert.deepEqual(\\n    gen.next(images).value,\\n    put({ type: 'IMAGES_LOADED', images }),\\n    'loadImages should dispatch an IMAGES_LOADED action with the images',\\n  );\\n\\n  assert.deepEqual(\\n    gen.next(images).value,\\n    put({ type: 'IMAGE_SELECTED', image: images[0] }),\\n    'loadImages should dispatch an IMAGE_SELECTED action with the first image',\\n  );\\n\\n  const error = 'error';\\n\\n  assert.deepEqual(\\n    gen.throw(error).value,\\n    put({ type: 'IMAGE_LOAD_FAILURE', error }),\\n    'loadImages should dispatch an IMAGE_LOAD_FAILURE if an error is thrown',\\n  );\\n\\n  assert.end();\\n});\\n\")), mdx(\"p\", null, \"Take note of the last \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"assert\"), \". It's testing the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"catch\"), \" by using \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"throw\"), \" instead of \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"next\"), \" on the generator. Another cool feature is the ability to send values in. Notice that we've created an \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"images\"), \" constant, and we pass that into \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"next\"), \". The saga \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"uses the value we pass in\"), \" for the next step in the sequence.\"), mdx(\"p\", null, \"It's awesome, and this level of control is a dream when it comes to testing async activity!\"), mdx(\"h2\", null, \"What's next?\"), mdx(\"p\", null, \"You can \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/joelhooks/egghead-react-redux-image-gallery\"\n  }, \"check out the full code for this example on github\"), \". I'm considering making a course on egghead to cover this. Would you be interested? \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://twitter.com/jhooks\"\n  }, \"Let me know on twitter\"), \"!\"), mdx(\"p\", null, \"If you wanted to expand the example a bit, you might:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"make it a slide show where the image advances to the next image on a timer\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"allow the user to search Flickr with keywords\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"drop in another API that delivers images\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"allow the user to select what APIs to search\")), mdx(\"p\", null, \"We've only touched the surface of generators, but even at this level, hopefully you can see how useful they can be when coupled with the redux-saga library, Redux, and React.\"), mdx(\"p\", null, mdx(\"img\", {\n    parentName: \"p\",\n    \"src\": \"https://s3.amazonaws.com/f.cl.ly/items/2s2q3B3x0Q04131p3V0C/jhooks_2016-Mar-20.jpg?v=db21427c\",\n    \"alt\": null\n  })));\n}\n;\nMDXContent.isMDXComponent = true;","frontmatter":{"title":"Build an Image Gallery Using React, Redux and redux-saga","date":"March 20, 2016","banner":null,"slug":null,"keywords":null}}},"pageContext":{"id":"493e8c7c-a6cd-5985-993f-b224a7e193d3","prev":{"id":"f124d64b-676e-5754-ab6a-4062665be57b","parent":{"name":"2016-04-08-setting-goals-for-my-version-of-success","sourceInstanceName":"legacy"},"excerpt":"There is this amazing conference in Las Vegas once a year called MicroConf. It's a gathering of like-minded entrepreneurs that (for the most part) are boot-strapping their business. This means that they aren't taking outside investment, specifically…","fields":{"title":"Setting goals for (my version of) success.","slug":"blog/2016/04/08/setting-goals-for-my-version-of-success","date":"2016-04-08T00:00:00.000Z"}},"next":{"id":"123cb5d0-32dc-52ee-89c1-94ee10149488","parent":{"name":"2015-12-31-2015-year-in-review","sourceInstanceName":"legacy"},"excerpt":"It’s been two years since I woke up dreading a workday. And I work almost every day. When we first set out to build egghead, I told my parter John that all he had to do was make amazing content, and that  I would do the rest . {% emoji flushed %} For…","fields":{"title":"2015 Year in Review","slug":"blog/2015/12/31/2015-year-in-review","date":"2015-12-31T00:00:00.000Z"}}}},
    "staticQueryHashes": ["1045846374"]}