Complete Introduction to React (feat. Redux and React Router)
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 on React by talking about the React ecosystem. While this course focuses on React development, he’ll be introducing other modern development tools and libraries like node.js, express, redux, WebPack, Mocha, Enzyme, npm, and react-router.
Composite Components and FactoriesThe React.createClass method allows for the creation of new elements. These elements can be reused to create composite components. Instances of the new element are created using the React.createElement method. After demonstrating how to create a composite component, Brian compares the React.createElement and React.createFactory methods
PropsThe props object allows for custom properties to be passed into a React component. Brian demonstrates how to pass a title property into the MyTitle component. After that, he spends a few minutes talking about the life cycle of the render function as well as the return statement syntax.
Adding a Color PropBrian adds a color property to the MyTitle component. He then passes the color to the <h1> element’s style attribute so the color of each heading can be customized.
Standard JS & Editor ConfigWriting code in a consistent style is one way to avoid errors and create a more maintainable codebase. Brian introduces the StandardJS linting library to check his code during development. Brian also uses EditorConfig to help maintain these styles across different IDEs.
NPM ScriptsBrian will be using NPM scripts to add automation to his workflow. NPM scripts are defined in the package.json file. The first script he creates is the ‘test’ script which will run the StandardJS linter.
Using React as a ModuleNow that Brian is using Webpack to bundle the application code, he no longer wants React to be a global module. Brian modifies his components to now ‘require’ React as a dependency. Webpack will now include React in the generated bundle.js file.
Configuring WebpackAs the application’s use of Webpack gets more complex, so will the NPM ‘build’ script. Brian spends a few minutes externalizing Webpack from the NPM script. He creates a webpack.config.js files and talks through some of the configuration options.
Stateless ComponentsA stateless component comes with less overhead than a traditional React component. They are leaner and have less surface area for errors or performance issues. Since these components lack state and other lifecycle methods, they are very easy for other developers to understand.
Creating Components with Arrow FunctionsBrian spends a few minutes talking about arrow functions. Arrow functions were added in ES6. Brian refactors a component to use arrow functions which allow the component to be defined with less code.
Refactoring the App ComponentNow that Brian is ready to begin adding features to the application, he renames the MyFirstComponent to App. This component will be the entry point for the application. Brian also adds some JSX and styling for overall user interface.
Webpack WatchRunning the webpack command after each change can be cumbersome. Brian introduces the ‘webpack --watch’ command which will monitor the directory for changes and rerun the build process when any files are modified.
React RouterThe React Router module is used to manage different URLs in the application. The <Router> component defines which component should be instantiated when the application encounters various paths. To demonstrate this, Brian configures the App component to display a Landing component when the default ‘/‘ route is encountered.
Importing JSON DataThis application has a data.json file that will be loaded to populate the user interface. Brian configures Webpack to load the JSON file during the build process. This way it is available as a module and can be required by any component.
Linking between RoutesThe React <Link> component can be used to move between different routes. The desired destination is specified using the ‘to’ attribute. Hashtags can be omitted from the routes since React manages the updating of the URL and prefixes the hashtag internally.
Populating the Search PageBrian updates the Search component to display the show titles rather than the raw JSON data. Since the show data is already imported into the component, he can use the Array.map method to produce <h3> tags for each item in the shows array.
ES6 Template StringsBrian continues to update the user interface of the search page. He introduces how to use ES6 template strings for injecting variables into strings rather than the traditional concatenation method. He also adds more show data like the poster thumbnail, year, and description.
Creating the ShowCard ComponentThe user interface for each show should really be encapsulated into its own component. Brian creates a ShowCard component that is passed the show data though the component’s ‘props’ property.
Key PropsReact manages list-based data through a key property. The key property is the unique identifier for an item so React is able to track the item within the interface. Brian demonstrates how to add a key property for each show and uses the show’s imdbID property as the key.
PropTypesPropTypes are a useful method for enforcing data integrity within a component. They are used to identify properties that can be passed to the component. The PropTypes declaration can also specify data type as well as if the property is required.
Component ChildrenOften times in an application, there are user interface elements or other layout-specific times that are shared between components or routes. The props.children property can be used to indicate where child components should be placed. This allows the parent component to have container elements reused for multiple child components.
Index RoutesNesting routes allows parent components to be reused thus making it easier to share user interface elements. An <IndexRoute> component is used to indicate which component should be loaded when the current route matches the parent’s path.
React.createClassSince the Search component will allow users to input data, it will have to manage it’s current state. Brian first modifies the component to be created using React.createClass. He also talks briefly about the pros and cons of using ES6 class syntax when declaring stateful components.
Managing StateState is managed in a component by first implementing the getInitialState() method. This method will initialize any state properties within the component. As changes in the component occur, the setState() method is used to update the current state.
Auto-BindingBrian spends a few minutes revisiting the ES6 class syntax that can be used to create React components. He discusses the need to manually create bindings when that syntax is used. With React.createClass, method bindings are automatically initialized.
Implementing the Search FilterBrian finishes the search functionality by implementing the ability to filter the results. He uses the Array.filter method to only return shows that have a title matching the search string entered by the user.
State QuestionsBrian wraps up the first day of the course by answering a few audience questions.
State Questions ContinuedBrian begins the second day of his React workshop by answering some audience questions about the topics he’s covered up to this point. He also talks briefly about how selected the various Node modules for this course and shares a few thoughts on tooling.
Writing Your First TestChai is the assertion library used for describing the outcome of each test. To ensure all dependencies are installed and configured properly, Brian begins with a simple test containing a true assertion. He then demonstrates how to run the test from the command line.
Testing with NPM ScriptsAfter demonstrating how to handle failing tests, Brian updates the ‘test’ script in the package.json file to run the Mocha command. This makes executing the tests from the command line simpler.
Testing React with EnzymeOne way to test React components is to verify it contains the proper markup. The shallow() method within the Enzyme helper library will render a React component while excluding any child components. This makes it easier to isolate a component’s functionality and test it’s generated markup.
Testing ShowCard CountWhen the search page is first accessed, there should be a ShowCard component for every item in the shows data source. Brian demonstrates how to write a test that compares the number of generated ShowCards components to the length of the shows array.
Testing Filtering with mount()The mount() method in Enzyme is similar to the shallow() method but comes with a little more overhead. This overhead allows jQuery-like syntax to be used for searching within a component as well as event simulation. Brian uses mount() to simulate the search functionality and verifies the correct number of results are generated.
Test Coverage with NYCThe NYC Node module can be used to generate test coverage reports. These reports will track how much of the application code is covered by unit tests. If any code is not being called or tested, NYC will indicate the ‘uncovered’ lines in both the command line and the generated reporting page.
Thoughts on Unit Testing ReactBrian spends a few minutes sharing some of his personal views on unit testing React code. He typically does not write unit tests for any React components that generate markup. He prefers only testing the modules containing business logic that provide data to React components.
Hot Module ReloadingWhile this course does not cover Webpack features like hot module reloading, Brian spends some time talking about why it can be useful and when you can run into problems using it with React.
Route ParametersThe Details component in will need to display information for a specific show. Brian creates a new route for this component and demonstrates how to pass route parameters dynamically into the component.
Data TunnelingIn React, data flows down through components and events bubble up. This architecture can cause problems when two separate routes needs to share data. In this case, it’s best to have the data source exist within a common parent. Then the appropriate data can be passed from the parent to each route that requires it.
Route onEnter EventSince the App component will be determining which show needs to be passed to the Details component, Brian adds a custom method that will be called by the onEnter event for the detail’s route. This method will get passed a nextState property as well as a replace function that can be used to redirect the user.
Routing QuestionsBefore moving on, Brian spends a few minutes fixing a couple of linting errors in the application. He also answers a number of audience questions about functional programming techniques, declaring propTypes, and the way the routes are functioning.
Displaying Data in the Details ComponentNow that the router is passing the proper show data to the Details component, Brian completes the user interface that will display the show title, description, year, poster, and trailer.
Creating a Header componentThe markup for the header of the application is duplicated across a couple components. Brian would like to create a separate Header component that can be reused. He moves the branding into this new component and adds a back button.
Conditional JSXSince the Header component will be reused on both the search page and the details page, it will need to hide/show the search field and the back button depending on where it’s located. Brian demonstrates how to conditionally include JSX in the component based on the value of one of it’s properties.
Completing the Header ComponentBrian adds the new Header component to the search page. He also modifies the function that handles the change event so it only receives the new search term and is no longer passed an event object. The actual onChange event is now handled inside the Header component to promote better encapsulation.
FluxFlux is an application architecture pattern for building client-side web applications. It emphasizes a unidirectional data flow which compliments React. Brian shares a diagram of the Flux data flow and highlights the use of a ‘store’ which is responsible for providing data to a view component.
Flux vs. ReduxRedux was modeled after Flux. Although, rather than creating new stores for each data model, Redux utilizes a single tree-like store. Data within this store is modified by a reducer.
ReducersBrian begins creating the store for the application. The store will be comprised of reducers which return a new state based on the action they receive. After creating an initial reducer, Brian separates it into one root reducer and an individual reducer for handling the setSearchTerm action.
Default and Initial StateRedux reducers must always return a state. Brian adds a default case to the root reducer’s switch statement that will return the current state if no action is matched. He also uses the ES6 default parameter syntax to supply an initial state if none is passed to the reducer.
ConnectorsBrian demonstrates how to use the Redux.connect() method to create a connector for use in React components. The connector provides any component with access to the current state and any methods for dispatching actions which update the state. The connector also provides a reference to the application’s store.
Using Redux in ReactNow that the store is created and the connector is built, Brian replaces the existing state management code in the application with the Redux implementation. He wraps the entire application in a Provider component which gives all subcomponents access to the store. He then implements the connector in each component that requires it.
Updating the Search ComponentBrian updates the Search component to use the newly created Redux store. The Search component now pulls the searchTerm property from its props instead of its state. This greatly simplifies the component since it has now become stateless.
Searching from the Landing PageBrian implements the search functionality for the Landing component. He adds an onChange event to the input to update the store. Brian also wraps the input field in a form so the onSubmit event can take the user to the search results page.
Linking to the Details PageClicking on a show still does not display the details for that show. To fix this, Brian goes into the ShowCard component and wraps all the content in a React Link component to take the user to the details page.
Redux ToolsBrian spends a few minutes demonstrating how to use the Redux dev tools for Google Chrome. Just like with the React tools, the Redux tools are an extension. They also require some middleware to be injected into the redux.createStore() method in the application.
Testing ReduxSince Redux requires all methods and reducers be functional by nature, unit testing is very easy. Brian writes a few tests for the Store.jsx file. He verifies the initial state is set properly and the state is updated when the setSearchTerm action is called.
What is Universal Rendering?Universal rendering, or server-side rendering, involves sending the fully rendered application in the initial payload to the browser and loading the application code afterward. This shortens the time the user will have to wait before something appears on screen. Brian demonstrates what happens without universal rendering and talks about how to solve these problems.
Externalizing the StateBefore Brian implements the universal rendering, he first externalizes all state so it only exists in Redux. This will make it easier to manage since all state is located in one place.
Browser vs. Server RenderingSince universal rendering involves the server sending a fully-rendered version of the application, there can’t be any reference to browser APIs. Brian creates a separate module that will act as the browser’s entry point in the application.
Using Express Part 1As Brian has pointed out, universal rendering happens on the server and is sent in the initial payload to the browser. This means the server must run the React application to generate the output. Brian is using Express, a lightweight Node application framework, to create the server environment.
Rendering QuestionsBefore moving on, Brian spends a few minutes answering audience questions about Express and the implications of producing different output between the server and client environments.
Lifecycle Methods & Webpack Chunking
Lifecycle MethodsReact components have lifecycle methods that are useful when code must be executed at a specific time. Brian utilizes the componentDidMount() method for AJAX requests because this method will not be called by the server. This means the AJAX requests will only occur in browser-based instances of the application.
Asynchronous RoutesBrian continues implementing Webpack chunking in the application. Webpack chunking requires asynchronous routes so it’s able to determine how to bundle the component code into separate files. Brian demonstrates how to reconfigure the route definitions to work asynchronously.
Final Q&ABrian wraps up the course by answering a few final audience questions.