Complete Intro to React v2 (feat. Router v4 and Redux)
This course has been updated! We now recommend you take the Complete Intro to React, v5 course.
Table of Contents
IntroductionBrian 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.
YarnBefore 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 YarnBrian 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 ReactBrian 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 ComponentBrian 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. createElementBefore 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.
Factories & PropsBrian 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.
NPM ScriptsNPM 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 ModulesThe 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 WebpackBrian 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 ConfigurationMoving 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 FileThe 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 NPMNow 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 vs. createElementIn 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.
Importing CSS in ReactNow 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.
Automated LintingWith 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 ServerUp 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 ReactBrian 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 ConfigurationFor 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 RouteWith 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 branch
Creating the ShowCard ComponentBrian 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 PropWhen 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.
PropTypesPropTypes 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 JSXBrian 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 StateBrian 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.
Snapshot Testing with JestBrian 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 JestBefore 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 EnzymeIn 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 ShowCardsBrian 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 FieldThe 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 IstanbulBrian 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 ParametersBrian 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 StateThe 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 ComponentBrian 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 ComponentBrian 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 ComponentsBrian 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 ComponentBrian 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 ComponentThe 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 1Now 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 2Brian 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 ClickableBrian 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 ES6Brian spends a few minutes answering audience questions about sharing state between components and using the createClass method in ES6.
React Lifecycle MethodsBrian 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 DocumentationBefore 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 RequestsBrian 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 ToolsDebugging 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.
ReducersA 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 reducer
StoresNext, 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 CreatorsFinally 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 ReducerBrian 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 ApplicationNow 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 ReduxActions are dispatched to Redux using the dispatch() method. A component will call dispatch() on its props object and pass the executed action.
Redux ReviewBrian 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 EventThe 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 ComponentBrian 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 ComponentThe 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 DevToolsJust 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 ReduxBefore 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.
Middleware & ThunksSimply 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 ReduxBrian 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 ActionsBrian 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 ComponentBrian 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 SnapshotThe 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 ActionsNext 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 ReducersBrian 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.
Separating the Client and Server CodeIn 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 RenderingIf 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 ServerBrian 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 WebpackBrian 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 RoutingAn 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 BundleBrian 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 BundlesBrian 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 ProductionBrian 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.
PreactPreact 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 PracticesBrian 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.