This course has been updated! We now recommend you take the Complete Intro to React, v8 course.
Table of Contents
React Introduction
Introduction
Brian Holt begins his course, A Complete Introduction to React, with a little background information about himself. Brian also explains how this course is an updated version of a previous React course he delivered at Frontend Masters. Participants in this course should be sure they are using the updated course page and Github branches.Yarn
Before getting into React, Brian outlines the setup instructions for installing and configuring some of the development tools. He’ll be using Yarn for managing dependencies instead of NPM. Brian believes Yarn is significantly faster and has better caching support.Using Yarn
Brian runs the Yarn installer and views all the dependencies required by the project. He showcases a few features of Yarn which separates application dependencies from development dependencies. Yarn can also indicate dependencies which have updates available.Understanding React
Brian introduces the React library and draws a few comparisons to other libraries/frameworks like jQuery, Backbone and Angular. He stresses that React is just a View layer and, while his first examples will be fairly bare bones, the code will slowly evolve into a full-stack application.My First Component
Brian builds out a very simple React component. This component does not use any advanced features like JSX, ES6, or Transpiling. It simply utilizes the render() method to return a <div> with an <h1> inside it.createClass vs. createElement
Before moving on to the next example, Brian spends a few minutes explaining the difference between the createClass() and createElement() methods. The createClass() method creates a blueprint for a new type of component while the createElement() method creates an instance of a component.My Second Component
For his second component, Brian refactors the first component into a separate JavaScript file. He then extracts the <h1> element and wraps it in a new MyTitle React component. This way, the MyTitle component can be reused across multiple “pages” in the application.Factories & Props
Brian introduces Factories which are convenience methods for instantiating component. He then demonstrates how to add props to a component. The props argument allows data to be passed into a component which makes it more reusable while maintaining a good separation of concerns. The code up to this point is located on the v2-1 branch.
Tooling
Using Standard
Brian steps away from React to walk install and configure some tooling he’ll need moving forward. He starts with standard which is a JavaScript linting tool. Standard will check the source code for rule violations like unused or undeclared variables.NPM Scripts
NPM Scripts are an easy way to shorten commands need to build, test, package, or lint the application. Brian prefers using NPM Scripts over task runners like Grunt and Gulp.Webpack & ES6 Modules
The next tool Brian introduces is Webpack. Brian will be using Webpack to compile and bundle ES6 modules through the use of Webpack Loaders. Before he an leverage these bundling features of Webpack, Brian first separates out the MyTitle component and converts it to use ES6 module syntax.Bundling with Webpack
Brian uses Webpack to package the MyTitle.js and ClientApp.js components into a single bundle.js file. This file now contains all the necessary application code to run the React application. The code for the application up to this point is on the v2.2 branch.Babel Configuration
Moving forward, Brian will be using ES6 (ES2015) syntax in the application. Since not all browsers support all ES6 features, Babel will be used to transpile the ES6 code to a more compatible ES5 version. Brian spends a few minutes demonstrating how to configure the Babel preset that will be used in Webpack.Creating a webpack.config.js File
The commands Brian now needs to execute to build the application are getting more complex. To combat this, Brian creates a webpack.config.js file so he can configure an entry point, output directory, and the specific loaders needed to transpile the application source code. After completing this configuration, Brian puts the code for the application on the v2.3 branch.Running Webpack from NPM
Now that all the necessary tools are installed and configured, Brian demonstrates how to run Webpack from NPM. He also speeds up the build process by configuring Webpack to omit the node_modules directory.
JSX, CSS, & Linters
JSX
JSX adds HTML/XML-like syntax to JavaScript. JSX can make composing UI components easier. As Brian is demonstrating some basic features of JSX, he takes a few minutes to add the “watch” argument to the building process which will automatically rebuild the application when any file is changed.JSX vs. createElement
In response to an audience question, Brian dives a little deeper into the difference between using JSX and the createElement() method. After that, Brian imports the MyTitle component and uses JSX to instantiate it in the application. The code for the application up to this point is on the v2-4 branch.Configuring CSS Imports
CSS will be imported just like any JavaScript component. To enable this behavior in React, Brian includes both a style loader and a CSS loader. While configuring these loader he spends a few minutes explaining their differences.Importing CSS in React
Now that Brian has the style and css loaders configured, he imports the CSS files into the React components and adds a few classes to style the UI. Brian also refactors the MyFirstComponent class to now be App. The code for the application up to this point is on the v2-5 branch.Lint Rules for React
The standard JavaScript linting tool doesn’t understand React syntax out of the box and will either throw additional error or not enforce proper React best-practices. Brian updates the configuration so standard will now properly lint the React components.Automated Linting
With standard properly configured, Brian adds linting to the watch command. Now linting errors will appear automatically whenever a source file is edited. Brian also configures the eslint-loader to exclude the node_modules directory since developers are typically responsible for testing dependencies.Webpack Development Server
Up to this point, the files for the application have been accessed through the local file system. Moving forward, Brian would like to use a development server. He configures the built-in Webpack development server to serve the bundled files out of the “public” directory in the project. The code for the application up to this point is on the v2-6 branch.
Routing, Props, & State Management
Routing in React
Brian introduces the React Router which he’ll be using to configure and manage the different pages in the application. Before diving deeper into the features, Brian separates out the Landing component and gets the initial routing for the application working.HashRouter Configuration
For now, Brian is using the HashRouter for updating the URL. The HashRouter includes the current route after the “#” in the URL. It pattern-matches the requested route and displays the specified component.Creating the Search Page Route
With the HashRouter configured and working, Brian adds another route for the search page. He adds another Match component in the HashRouter and specifies the pattern for when the Search component should be shown. Once this is working, Brian transitions from using the HashRouter to using the BrowserRouter. Code for the application up to this point is on the v2-6 branchLoading JSON Files
Brian spends a few minutes answering some follow-up questions about routing in React. He also configures the json-loader which is responsible for loading JSON files into the application. This allows the application to include the data during the bundling process and import it into a component like any other JavaScript object.Iterating Through Data
Now that the JSON date for the Netflix shows is loaded in the application, Brian iterates through the shows and displays the titles on the search page. Since JSON data is treated like any other JavaScript object, Brian is able to use the Array.map() method for the iteration.Creating the ShowCard Component
Brian adds the UI for displaying shows. Hey then makes a case for creating a separate component to encapsulate a show’s functionality. This leads Brian into making the ShowCard component and introducing how to pass properties into a component.Key Prop
When components are generated from a list of data, React needs a method for keeping track of each individual component. The “key” property allows developers to specify a key value for each component. Typically the primary key or some other unique identifier from the data would be used for the key property. The code for the application up to this point is on the v2-8 branch.PropTypes
PropTypes are a more declarative way to specify properties that are passed to a component. Along with declaring individual properties, each property’s type is included as well. Brian adds the propTypes object to the ShowCard component and explains his use of the shape PropType.Using the Spread Operator in JSX
Brian spends a few minutes explaining how to use the ES6 spread operator to pass properties into a component. The spread operator simplifies the syntax and makes it easier to add/remove properties in the propTypes object. The spread operator also removes the need to wrap the properties in a shape. The code for the application up to this point is on the v2-9 branch.Managing State
Brian introduces how state is managed in a React application. As data is changed inside a component, React has a controlled process of getting, storing, and modifying the current state. The first instance of a mutable state will be in the Search component where Brian adds and input field for the search term.getInitialState() and setState()
The two main methods for managing state in React are getInitialState() and setState(). Brian uses the getInitialState() function to set the searchTerm property initially in the Search component. When the onChange event fires in the input field, the setState() method will updated the searchTerm property, which in turn, updates the state.Filtering
Brian wraps up the search functionality by implementing filtering. He uses the Array.filter() JavaScript method to add a filter to the shows object. Since the searchTerm property is part of the component’s state, the filter will dynamically update as that property is modified. The code for the application up to this point is on the v2-10 branch.
Testing React
Snapshot Testing with Jest
Brian switches gears for a few minutes to talk about testing. He introduces Jest which will enable snapshot testing. A snapshot test saves the visual look of the code and alerts the developer if the code is modified. Brian begins creating the Search.spec.js file which will run the snapshot test.Configuring & Running Jest
Before running the snapshot test, Brian installs and configures Jest. Part of the configuration includes having Babel transpile the ES6 modules so they can be consumed by Jest. Babel will only transpile these modules when Node is running in a test environment. The code for the application up to this point is on the v2-11 branch.Shallow Testing with Enzyme
In the previous example, Brian modified the ShowCard component which lead to a test failing in the Search component. Brian doesn’t think this should happen since these are separate components. To fix this, he adds Enzyme to his test running and utilizes a feature called Shallow Testing.Testing the Number of ShowCards
Brian writes another unit test which will verify the number of ShowCard components matches the number of shows in the JSON data. After the test is written, Brian demonstrates how to break the test by including a default search term.Testing the Search Field
The last test Brian includes will verify the search field is working properly. Brian uses the test framework to simulate a search term being entered and compares the filtered results to the expected show count. The code for the application up to this point is on the v2-12 branch.Test Coverage with Istanbul
Brian wraps up the section on testing React applications with a demonstration of test coverage. He’s using the Istanbul which creates a code coverage report indicating how much of the source code is covered by a test. Brian also show the lcov-reports which better visualize the coverage and provide some interactivity.
Data in React
URL Parameters
Brian creates the Details component which will show detailed information about a show. The details component will know which show to display based on data in the URL. This leads Brian to introduce URL parameters and demonstrate how to specify the parameters in the React Router.Sharing State
The Details component is going to need access to the JSON data. Rather than make a duplicate request for the data, Brian moves the JSON data to the closest common ancestor component. This makes the sharing of state between components easier. The code for the application up to this point is on the v2-13 branch.Updating the Search Component
Brian spends a few minutes answering audience questions about how properties are passed between components and how React transpiles JSX code. He then finishes some refactoring of the Search component now that it is managing it’s own state and no longer importing the JSON data.Passing Data to the Details Component
Brian uses the same logic from the Search component to pass data into the Details component. He filters the list of shows based on the “id” URL parameter. He also provides the React Router documentation as an additional resource for configuring the component views.Organizing Code in React Components
Brian spends a few minutes talking about how he organizes his code in a React component. He typically starts with the propTypes then includes the lifecycle methods, custom functions, and finally the render() method.Finishing the Details Component
Brian finishes the Details component by declaring the propTypes object. He then adds the rest of the user interface components which displays the title, year, poster image, description, and the trailer for the selected show. The code for the application up to this point is on the v2-14 branch.Creating a Header Component
The header of the application needs to be used across multiple components. Rather than having duplicate code, the header should be extracted out into it’s own component. Brian removes the header code from the Landing and Search, and Details components and creates a single Header component.Conditional Display Logic Part 1
Now that the Header component is being used in multiple places, it will need some conditional logic to determine whether or not the search bar should be shown. Brian begins adding this functionality be creating a boolean propType property named showSearch. The code for the application up to this point is on the v2-15 branch.Conditional Display Logic Part 2
Brian finishes the conditional display logic in the Header component by using the showSearch propType to determine if the search bar should be displayed. Once he has this code in place, Brian refactors the Search component to use the Header component. The code for the application up to this point is on the v2-16 branch.Making the ShowCard Component Clickable
Brian completes the ShowCard functionality by making the ShowCard components clickable. He uses the Link component which directs the router to the details route with the current ID appended.Q&A: Shared State & createClass in ES6
Brian spends a few minutes answering audience questions about sharing state between components and using the createClass method in ES6.React Lifecycle Methods
Brian introduces the React Lifecycle Methods. These are methods that are called at different stages throughout a component’s life in an application. One of the most common uses for these methods is to be able to request remote data as soon as a component is mounted. Brian spends a few minutes talking about these methods and how he typically uses them.Lifecycle Methods Documentation
Before going back to coding, Brian looks at the React documentation to show the audience where they can read more about these component lifecycle methods.componentDidMount() & AJAX Requests
Brian adds the componentDidMount() lifecycle method into the Details component. He’ll be using this method to trigger an AJAX request to load data for the specified show. Brian is using the axios library to do the AJAX request. The code for the application up to this point is on the v2-17 branch. - PLEASE NOTE: You must now add the apikey=frontend parameter when making requests to the omdb api.React Development Tools
Debugging React code can be a challenge because it’s transpiled and bundled into a single file. The React Developer Tools browser plugin can make debugging much easier. This extension understands React application and will allow you to inspect component code. Brian spends a few minutes demonstrating some of these features.
Redux
Redux Overview
Redux is a predictable state container for JavaScript applications. It will run both on the client and server. A single store is created to maintain the state for the entire application. The state is accessed and modified through the use of reducers and actions.Reducers
A reducer is a special function which takes in the current state as well as an action and returns a new state. Brian creates a rootReducer object for the application and explains how state will be passed to and returned from the reducerStores
Next, Brian creates the store for the application. The createStore method in Redux is passed the rootReducer to create the store. All application state will reside in this store object.Actions & Action Creators
Finally Brian creates the actions for the application. He stores the action definition in an actions.js file and the implementation of the action in an actionCreators.js file. These don’t need to be in separate files, but Brian prefers this architecture. Actions trigger the reducer to carry out the modification of state.Using Actions in a Reducer
Brian imports the action into the reducer. When the reducer is passed a specific action type, it will call the appropriate method to return a new state. Once the reducer is updated, Brian spends a few minutes reviewing the code and explaining his use of Object.assign().Adding Redux to the Application
Now that Brian has the store, action, and reducer ready, he adds Redux to the application. The Redux Provider component is added to the router and passed the store. Then in each component, the state property is mapped to the corresponding component property. The code for the application up to this point is on the v2-18 branch.Dispatching Actions to Redux
Actions are dispatched to Redux using the dispatch() method. A component will call dispatch() on its props object and pass the executed action.Redux Review
Brian spends a few minutes walking through the entire Redux integration in the application. After reviewing Redux, Brian refactors the Landing component to map the dispatching of actions to component properties.Search Submit Event
The search input field in the Landing Component is still not navigating to the search page. To fix this, Brian wraps the input in a Form component so he can utilize the onSubmit event. The code for the application up to this point is on the v2-19 branch.Using Redux in the Search Component
Brian moves on to the Search component and refactors it to read state from Redux. Once Redux is added, the Search component no longer needs the getInitialState() or handleSearchTermChange() methods because that functionality now exists in Redux.Using Redux in the Header Component
The last component to refactor is the Header component. Brian imports the connect object from react-redux as well as the setSearchTerm action creator. He then updates the Header component to read the state from Redux and dispatch the action when a new search term is entered. Code for the application up to this point is on the v2-20 branch.Redux DevTools
Just like with React, Redux has a set of browser developer tools for viewing and debugging code in the browser. A small amount of code needs to be added in the application for the developer tools to work. After adding the bootstrapping code, Brian spends a few minutes demonstrating the capabilities of the Redux developer tools.Q&A: Using Compose & Why to Use Redux
Before moving on to the next topic, Brian spends a few minutes answering audience questions about why he’s using Compose. He also makes a few more arguments for the use of Redux in any application.
Async Redux
Middleware & Thunks
Simply put, a thunk is a function wrapped around a value. The function abstracts where the value comes from. Brian spends some time explaining thunks and talking about why they are important in asynchronous operations.Using Middleware & Thunks in Redux
Brian adds the redux-thunk library to the application. This middleware will facilitate the exchange between synchronous and asynchronous actions. Before worrying about the asynchronous actions, Brian adds the addOMDBData reducer the existing reducers in the application.Async Actions
Brian now creates the getOMDBData function. Redux will call this function and trigger the asynchronous axios AJAX call. The result of the AJAX call will then dispatch the addOMDBData action.Adding Async Actions to the Details Component
Brian completes the redux-thunk middleware integration by updating the Details component. The Details component will now call the getOMDBData() function which triggers the asynchronous action. Since Redux is being used in the Details component, it no longer needs to read the state from the local store. Code for the application up to this point is on the v2-21 branch.Testing Redux: Updating the Snapshot
The introduction of Redux means most of the existing tests in the application are failing. This is because Redux is injected into the components but now present during the tests. Brian demonstrates how to use “unwrapped” components in the tests that are free of the Redux dependency. He then updates the snapshot to include the new Header component implementation.Testing Redux: Dispatching Actions
Next Brian updates the tests to utilize the Redux store and actions. He wraps the Search component in a Provider so it can have access to the store. He also updates the setting of the search term so it dispatches a Redux action. Code for the application up to this pint is on the v2-22 branch.Testing Reducers
Brian now creates a reducers.spec.js file for testing the reducers. He talks about how easy it is to write test for reducers. In fact it’s so easy, the Redux developer tools can write the test for you. Brian demonstrates how to create a couple tests for the reducers using the Redux developer tools.
Universal Rendering
Why Use Universal Rendering?
This application relies heavily on JavaScript for rendering. This makes it impossible for anyone without JavaScript to view the application. Brian talks about how Universal Rendering can solve this problem making the application more versatile and accessible.Separating the Client and Server Code
In order for Universal Rendering to work, the application must be able to run both from the server-side or client-side. Brian spends a few minutes separate the client and server aspects of the application. The core logic of the application will now reside in an App.js file.Implementing Server-Side Rendering
If the application is running on the server, it’s not able to use ES6 modules since they aren’t compatible with Node. Brian updates the .babelrc configuration file to compile the server-side code to CommonJS modules. Brian then adds the server logic. He introduces Express and implements the server-side rendering of the application.Running the Node Server
Brian starts the Node web server to run the application. He also moves the CSS files so they are included in the HTML file the traditional way. Code for the application up to this point is on the v2-23 branch.
Code Splitting & Preact
Code Splitting with Webpack
Brian introduces a feature of Webpack called code splitting. Code splitting gives developers the ability to separate application code into multiple bundles. The application will only download the bundles necessary to render the initial view. As a user navigates the application, additional bundles are loaded on-demand.Async Routing
An AsyncRoute is a higher-order component that will display a loading state until a component is completely loaded. Once the targeted component is loaded, the AsyncRoute will remove the loading state and display they component. Asynchronous loading happens through the use of the System.import() method.Creating a Landing Bundle
Brian spends a few minutes debugging some issues in the application. He then demonstrates how the Landing bundle is only loaded when the user navigates to the Landing page. Code for the application up to this point is on the v2-24 branch.Creating Search and Details Bundles
Brian finishes creating the Search and Details code bundles. He removes the module imports from the App.js file and instead uses System.import() to import each module with an AsyncRoute. Code for the application up to this point is on the v2-25 branch.Building for Production
Brian demonstrates how to build the application for production by using the Webpack “-p” flag as well as the Webpack UglifyJs Plugin. After some configuration and debugging, he gets the production bundles to generate and compares the production files size with the original application size.Preact
Preact is a near-drop-in replacement for React with a much smaller footprint. Preact is able to achieve this smaller size by eliminating some legacy support and turning over more work to the browser. Brian refactors the application to use Preact and compares the productions size to the version that used React. The code for the final version of the application can be found on the v2-preact branch.Final Q&A: Starter Kits, Organizing Code, & Best Practices
Brian concludes his course: The Complete Intro to React. He spends the last few minutes answering a few audience questions on project starter kits, organizing code, and sharing a few last best practices.