Chapter 4. Applying React

This chapter covers

  • Configuring React Router for the browser
  • Rendering the route contents in a consistent way by using props.children
  • Building reusable components
  • Using higher-order components to abstract common business logic
  • Taking advantage of the React component lifecycle

In chapter 3, you learned the basics of building views with React. Now you’ll build on those skills by exploring more-advanced concepts with React. This chapter will teach you what you need to know in order to build a production app with React.

You’ll be working with the All Things Westies app described in chapter 1. This is the first of many chapters in which you’ll be building this app. The code can be found at https://github.com/isomorphic-dev-js/complete-isomorphic-example.git. To start, you should be on branch chapter-4.1.1 (git checkout chapter-4.1.1). The master branch for this repo contains the complete code from all the chapters in the book.

To run the app for this chapter, use these commands:

$ npm install
$ npm start

When the server is running, the app in this chapter will be loaded from http://localhost:3000/ (although there’s nothing to see on the chapter-4.1.1 branch). It’s not isomorphic because I want you to stay focused on the React concepts. As you build out this app in chapters 7 and 8, you’ll turn the app into an isomorphic app.

The app is shown in figure 4.1. I’ve called out the component parts that need to be added to make this work.

Figure 4.1. The All Things Westies sample app that you’ll begin building in this chapter. You’ll build out the various parts of this app in later chapters.

There are three main routes (and a home route, /): /products, /cart, and /profile. In the next section, you’ll set up the routing.

4.1. React Router

To build a web application, you usually need a router. Routers provide a mapping between the URL-based route and the view that the route should load. Because React is the view library, it doesn’t handle application routing on its own. That’s where React Router comes into play.

React Router has become the community choice for routing in React apps. It even has support for server-side routing, making it a great choice for isomorphic apps (covered in chapter 7). React Router makes creating your routes straightforward because it uses JSX to let you declare routes. React Router is a React component that handles your routing logic.

React Router versions

This app and the rest of the book use React Router 3 (v3.0.5). Since I started writing this book, a newer version (v4) has come out. The latest version is a complete rewrite of the way React Router works. It’s more in line with how React works, but it requires a new way of thinking about how the router interacts with an isomorphic app.

I’ve provided a version of the app with explanation in three appendices (A–C). You’ll find examples related to this chapter in appendix A. I explain how to get started with React Router 4 and the major changes with the removal of the React Router lifecycle.

The good news is the React Router team has committed to supporting v3 for the indefinite future (because of the breaking nature of v4). But I do recommend you explore v4 if you’re starting a new project.

4.1.1. Setting up an app with React Router

React Router uses components to introduce routing into your app and to define the child routes. Before you initiate the app with the router, you must first define a set of routes that’ll be used. You’ll set them up in a sharedRoutes.jsx file.

In this first section, you’ll add the App component with the router. This will let you easily support the server-rendering use case you’ll build later in the book. The following listing shows you the code to add to sharedRoutes.jsx. Remember, if you want to follow along, you should be on branch chapter-4.1.1.

Listing 4.1. App routes—src/shared/sharedRoutes.jsx
import React from 'react';                         1
import { Route } from 'react-router';              2
import App from '../components/app';               3

const routes = (                                   4
  <Route path="/" component={App}>                 5
  </Route>
);

export default routes;

  • 1 Include React because React Router uses React components to implement the router.
  • 2 Require the Route component from React Router.
  • 3 Include root component: app.jsx.
  • 4 Create the route object with JSX syntax.
  • 5 Route component requires two properties, the path to this route and the component that’s displayed. This results in the root route returning App as its component.

I’ve provided the skeleton of the App component for you so you don’t need to add this code. The following listing shows the App component.

Listing 4.2. App component—src/components/app.jsx
import React from 'react';

const App = () => {
  return (
    <div>
      <div className="ui fixed inverted menu">
        <h1 className="header item">All Things Westies</h1>        1
        <a to="/products" className="item">Products</a>            2
        <a to="/cart" className="item">Cart</a>                    2
        <a to="/profile" className="item">Profile</a>              2
      </div>
      <div className="ui main text container">
        Content Placeholder                                        3
      </div>
    </div>
  );
};

export default App;

  • 1 Title of app
  • 2 Root navigation links—each will be added to the sharedRoutes file in the next section.
  • 3 Each route’s contents will render here—for now, there is placeholder text.

Next, you’ll set up your app to use React Router. The following listing shows you how to set up the main.jsx file.

Listing 4.3. Render the app with React Router—src/main.jsx
import React from 'react';                                    1
import ReactDOM from 'react-dom';                             1
import {
  browserHistory,
  Router
} from 'react-router';                                        2
import sharedRoutes from './shared/sharedRoutes';             3

ReactDOM.render(
  <Router                                                     4
    routes={sharedRoutes}                                     5
    history={browserHistory}                                  6
  />,
  document.getElementById('react-content')
);

  • 1 Include your React and ReactDOM dependencies.
  • 2 Include the Router component and the browserHistory module from React Router.
  • 3 Include the sharedRoutes file you created.
  • 4 Render the React app into the DOM by declaring the Router component as your root component.
  • 5 Router takes in the routes you included from sharedRoutes.
  • 6 Router component needs to know which implementation of history it should use—here use browser history module so the app can use the built-in browser history API.

Instead of rendering a root component into the DOM, React Router ends up being your root component. Another way to think of it is as the top component in your component tree (see figure 4.2).

Figure 4.2. Example component tree with React Router as the root element

Under the hood, the router is using the browser history object. It hooks into this object to use push state and other browser-routing APIs.

Additionally, React Router allows you to pass in this history object. That way, it doesn’t make any assumptions about which environment it runs in. On the browser, you pass in a different history object than on the server. That’s part of what makes React Router good for isomorphic apps. Passing in the history object is also a more testable pattern.

4.1.2. Adding child routes

To make the rest of the app work, add the child routes the user will use to navigate between the views in the app. This requires two additional steps: creating child routes and setting up app.jsx to render any child. The following listing shows how to add the new routes to the sharedRoutes file. If you want to follow along, the base code for this section is in branch chapter-4.1.2 (git checkout chapter-4.1.2).

Listing 4.4. Adding child routes—src/shared/sharedRoutes.jsx
//... other import statements
import Cart from '../components/cart';                     1
import Products from '../components/products';             1
import Profile from '../components/profile';               1

const routes = (
  <Route path="/" component={App}>
    <Route path="/cart" component={Cart} />                2
    <Route path="/products" component={Products} />        2
    <Route path="/profile" component={Profile} />          2
  </Route>
);

export default routes;

  • 1 Include the component for each route.
  • 2 Create child routes by nesting them inside the App route.

Each of the child routes will be combined with App. React Router will know the appropriate child component that should be made available to App to be rendered.

React: rendering any children

The next step in getting the child routes working is to set up the App component to display any arbitrary child. The App component doesn’t need to know which child it’s rendering—only that it needs to render a child. You decouple the implementation of the child and parent. This creates a reusable pattern in which the same child can be used in multiple views, or vice versa. Figure 4.3 shows the React Router and child route relationship.

Figure 4.3. Using props.children to render components at runtime

You can pass in children by nesting React components:

<MyComponent>
  <ChildComponent />
</MyComponent>

Then inside the render function of MyComponent you reference the child on the props object:

render() {
  return <div>{props.children}</div>
}
Note

React Router handles passing down the children component by assigning props via JavaScript and using the lower-level React APIs such as createElement. You don’t need to worry about this, but if you’re interested in exploring further, check out https://github.com/ReactTraining/react-router/blob/v3/docs/API.md#routercontext.

This pattern allows the child component to be determined dynamically at runtime. The following listing shows how to update the App component to do this. Add the code from the listing to the app.jsx component code that already exists.

Listing 4.5. Rendering any child—src/components/app.jsx
const App = (props) => {
  return (
    <div>
      <div className="ui fixed inverted menu">
        ...
      </div>
      <div className="ui main text container">
        {props.children}                                  1
      </div>
    </div>
  );
};

App.propTypes = {                                         2
  children: PropTypes.element                             3
};

  • 1 App component renders the children property
  • 2 Setting propTypes on the component
  • 3 Prop children is a React element—the propTypes object describes this information.

Setting propTypes on components provides documentation and is considered best practice. It’s an object that describes the expected properties, including whether they’re required.

Router properties

Because the Router wraps the App component, it passes down several router objects as props. Many of these objects are required in child components, but I’ll focus on three:

  • locationThis mirrors the window.location object, built from the history you passed in to the router. It contains properties such as query and pathname that you can use in components.
  • paramsThis object contains all the dynamic parameters on the route. If you had a route such as /products/treats that matched a route such as /products/ :category, this object would contain a property called category: { category: treats }.
  • routerThis object contains many methods for interacting with the router and history, including lower-level APIs. Most commonly, I find the need to use the push() method to navigate around an app from JavaScript.

In the next section, you’ll use the Link component, which takes advantage of the lower-level router and history APIs so you don’t have to.

4.1.3. Routing from components: Link

React Router goes one step further and provides a React component for you to use when you want to trigger navigation. That way, you don’t need to worry about what’s going on under the hood.

The Link component renders an <a> tag. To use the Link component, you include it in your component and then render it with the properties it needs. If you want to follow along with this section and get the code so far, switch to the branch called chapter-4.1.3 (git checkout chapter-4.1.3). The following listing shows you how to update the header to use the Link component instead of standard links in app.jsx.

Listing 4.6. Using the Link component—src/components/app.jsx
import React from 'react';
import { Link } from 'react-router';                               1

const App = (props) => {
  return (
    <div>
      <div className="ui fixed inverted menu">
        <h1 className="header item">All Things Westies</h1>
        <Link to="/products" className="item">Products</Link>      2
        <Link to="/cart" className="item">Cart</Link>              2
        <Link to="/profile" className="item">Profile</Link>        2
      </div>
      <div className="ui main text container">
        {props.children}
      </div>
    </div>
  );
};

  • 1 Include the Link component from React Router
  • 2 Convert <a> tags to <Link> tags

Note that instead of an href property, the Link component requires a to property. After adding the Link components, your app will properly route between views.

The React Router library has one more important part that you’ll want to know about for building production apps: how to hook into the router lifecycle.

4.1.4. Understanding the router lifecycle

React Router provides hooks into its lifecycle to allow you to add logic between routes. A common use case for lifecycle hooks is adding page-view tracking analytics to your application so you know how many views each route gets.

Note

If you’re using React Router 4, check out appendix A to see how to move this code into the React lifecycle and how to handle the concepts discussed in this section.

Imagine if you tried to add this logic into your components. You’d end up with the tracking logic in every top-level component (Cart, Products, Profile). Or you’d end up trying to detect changes based on properties in the App component. Both methods are undesirable and leave a lot of room for error.

Instead, you want to use the onChange and onEnter lifecycle events for React Router. (A third lifecycle hook, onLeave, isn’t covered here.) Figure 4.4 shows the order in which these handlers fire.

Figure 4.4. The onEnter handler fires only once for the root route, but the onChange handler fires on every subsequent route change.

For each route, onEnter is fired when the app goes to the route from a different route. Because / is the root route, it can be entered only once. The onChange handler is fired each time a child route changes. For the root route, this happens on each route action after the first. The following listing shows how to implement these handlers in the sharedRoutes.jsx file. If you’re following along and want to see the code from the previous sections, you can find it on branch chapter-4.1.4 (git checkout chapter-4.1.4).

Listing 4.7. Using onChange in the router—src/shared/searchRoutes.jsx
const trackPageView = () => {                                             1
  console.log('Tracked a pageview');
};

const onEnter = () => {                                                   2
  console.log('OnEnter');
  trackPageView();
};

const onChange = () => {                                                  3
  console.log('OnChange');
  trackPageView();
};

const routes = (
  <Route path="/" component={App} onEnter={onEnter} onChange={onChange}>  4
    <Route path="/cart" component={Cart} />
    <Route path="/products" component={Products} />
    <Route path="/profile" component={Profile} />
  </Route>
);

  • 1 Reusable function for tracking page views (in the real world, you’d call your analytics tool)
  • 2 Handler for onEnter—logs OnEnter
  • 3 Handler for onChange—logs OnChange
  • 4 Each Route can have an onEnter and/or onChange property.

Next, you’ll explore React’s component lifecycle, which is a completely different set of lifecycle functions specific to React. The lifecycle functions give you greater control over when things happen in your app.

4.2. Component lifecycle

A site that has user accounts requires a login. Certain parts of the site will always be locked down so you can view them only if you’re logged in. For example, with the All Things Westies app, users who want to view their settings page to update their password or view past orders will need to log in.

This use case is the opposite of the analytics use case in the preceding section. Instead of doing something on every route, you want to check for logged-in status only on certain routes. You could do that on the routes, if you’d like, with onChange or onEnter handlers. But you can also put this logic inside the appropriate React component. For this example, we’ll use the component lifecycle.

React provides several hooks into the lifecycle of components. The render function, which you’ve already used, is part of this lifecycle. The lifecycle of a component can be broken into three parts (illustrated in figure 4.5):

  1. Mounting eventsHappen when a React element (instance of a component class) is attached to a DOM node. This is where you’d handle the check for being logged in.
  2. Updating eventsHappen when React element is updating either as a result of new values of its properties or state. If you had a timer in your component, you’d manage it in these functions.
  3. Unmounting eventsHappen when React element is detached from the DOM. If you had a timer in your component, you’d clean it up here.[1]

    1

    React lifecycle list and illustration concept are from Azat Mardan’s React Quickly (Manning, 2017, https://www.manning.com/books/react-quickly).

Figure 4.5. The React lifecycle consists of three types of lifecycle events. Each has corresponding method hooks.

4.2.1. Hooking into mounting and updating to detect user’s logged-in status

To detect whether the user is logged in, you’ll take advantage of one of the React lifecycle functions. This function is fired before the component has mounted (been attached to the DOM). Listing 4.8 shows how to add the check to the user profile component inside componentWillMount. There’s a placeholder for Profile, and you’ll want to update it with this code. If you’re following along and want to check out the code from the previous sections, switch to branch chapter 4.2.1 (git checkout chapter-4.2.1).

Listing 4.8. Using lifecycle events—src/components/profile.jsx
class Profile extends React.Component {

  componentWillMount() {
       if (!this.props.user) {                 1
      this.props.router.push('/login');        2
    }
  }

  render() {}
}

  • 1 Check for a user property—if it doesn’t exist, assume the user needs to log in.
  • 2 Force user to route to log in using the router object.

In profile.jsx, you added a reference to the router prop. But if you run the code now and load the /profile route, the app will throw an error because you haven’t passed in the router object. To do that, you need to update app.jsx to pass props to its children. The following listing takes advantage of two React top-level API calls: React.Children and React.cloneElement.

Listing 4.9. Passing props to children—src/components/app.jsx
const App = (props) => {
  return (
    <div>
      <div className="ui fixed inverted menu"></div>
      <div className="ui main text container">
        {
          React.Children.map(                             1
            props.children,                               2
            (child) => {                                  3
              return React.cloneElement(                  4
                child,                                    4
                { router: props.router }                  4
              );
            }
          )
        }
      </div>
    </div>
  );
};

  • 1 Use the React.Children.map top-level API method to iterate over the current children property.
  • 2 The map function takes in props.children as its first argument.
  • 3 Use the React.cloneElement top-level API to copy the current child and pass in additional props.
  • 4 The second argument is a callback function that gets called for each child.
First render cycle

In an isomorphic app, the first render cycle is the most important. That’s where you’ll use lifecycle events to control what environment the code runs in. For example, some third-party libraries aren’t loadable or usable on the server because they rely on the window object. Or you might want to add custom scroll behavior on the window event. You’ll need to control this by hooking into the various lifecycle methods available on the first render.

The first render lifecycle is made up of three functions (render and two mounting events):

  • componentWillMount()Happens before the render and before the component is mounted on the DOM
  • render()Renders the component
  • componentDidMount()Happens after the render and after the component is mounted on the DOM

For the isomorphic use case, it’s important to note some differences between componentWillMount and componentDidMount. Although both methods run exactly once on the browser, componentWillMount runs on the server, whereas componentDidMount never runs on the server. In the previous example, you wouldn’t want to run the user logged-in check in componentWillMount because the check would also run on the server. Instead, you’d put the check in componentDidMount, guaranteeing that it happens only in the browser.

componentDidMount never runs on the server because React never attaches any components to the DOM on the server. Instead, React’s renderToString (used on the server in place of render) results in a string representation of the DOM. In the next section, you’ll use componentDidMount to add a timer for a modal—something you want to do only in the browser.

4.2.2. Adding timers

Imagine that you want to add a countdown timer to the Products page. This timer launches a tooltip modal after a set amount of time. Figure 4.6 shows what this looks like. Timers are asynchronous and break the normal flow of user event-driven React updates. But React provides several lifecycle methods that can be used to handle timers within the lifecycle of a React component.

Figure 4.6. The tooltip that shows as a user prompt

To add a timer to your component, you need to kick it off after the component has mounted. Additionally, you’ll need to handle the cleanup of the timer when the component unmounts or when certain other actions happen. To check out the base code for this section, switch to branch chapter 4.2.2 (git checkout chapter-4.2.2). The following listing shows how to add the timer code to products.jsx. The base component already exists, so update the code in bold.

Listing 4.10. Adding the timer—src/components/products.jsx
import React from 'react';

class Products extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      showToolTip: false,
      searchQuery: ''
    };
    this.updateSearchQuery = this.updateSearchQuery.bind(this);
  }

  componentDidMount() {
    setTimeout(() => {
      this.setState({                                               1
        showToolTip: true
      });
    }, 10000);                                                      1
  }

  updateSearchQuery() {                                             2
    this.setState({
      searchQuery: this.search.value                                3
    });
  }

  render() {
    const toolTip = (                                               4
      <div className="tooltip ui inverted">
        Not sure where to start? Try top Picks.
      </div>
    );
    return (
      <div className="products">
        <div className="ui search">
          <div className="ui item input">
            <input
              className="prompt"
              type="text"
              value={this.state.searchQuery}                        5
              ref={(input) => { this.search = input; }}             3
              onChange={this.updateSearchQuery}                     2
            />
            <i className="search icon" />
          </div>
          <div className="results" />
        </div>
        <h1 className="ui dividing header">Shop by Category</h1>
        <div className="ui doubling four column grid">
          <div className="column segment secondary"></div>
          <div className="column segment secondary"></div>
          <div className="column segment secondary">
            <i className="heart icon" />
            <div className="category-title">Top Picks</div>
            { this.state.showToolTip ? toolTip : ''}                6
          </div>
          <div className="column segment secondary"></div>
        </div>
      </div>
    );
  }
}

export default Products;

  • 1 In componentDidMount, trigger the timer—the setTimeout callback sets the component state after 10 seconds.
  • 2 Change handler for search input, sets state of searchQuery.
  • 3 Search is set by taking the value from the input element that’s been saved to this.search in the ref callback.
  • 4 The div displays the toolTip (declaring it as a variable makes the ternary statement more readable). It shows only when showToolTip is true (after the timer has triggered).
  • 5 The value for input is tied to state so input uses the component state as its source of truth.
  • 6 The div displays the toolTip (declaring it as a variable makes the ternary statement more readable). It shows only when showToolTip is true (after the timer has triggered).

The tooltip shows up at this point (it’s set to show after 10 seconds). But let’s imagine you want to show the tooltip only if the user has never interacted with the page. In that case, you need a way to clear the tooltip when the user has interacted. Technically, you could do that in the onChange handler for search, but for illustrative purposes, you’ll add this in componentWillUpdate. The following listing shows how to do that.

Listing 4.11. Clearing the timer on user interaction—src/components/products.jsx
class Products extends React.Component {
 componentDidMount() {
   this.clearTimer = setTimeout(() => {               1
     this.setState({
       showToolTip: true
     });
   }, 10000);
 }

 componentWillUpdate(nextProps, nextState) {          2
   if (nextState.searchQuery.length > 0) {
     clearTimeout(this.clearTimer);                   3
   }
   console.log('cWU');                                4
 }
 updateSearchQuery() {}
}

  • 1 Capture return value of setTimeout so timer can be cleared.
  • 2 When component receives new state, check state for presence of search query.
  • 3 Clear timer.
  • 4 Log shows that the componentWillUpdate method fires each time a letter is typed into the search box (with abbreviation cWU).

If you restart the app and interact with the Products page before the 10-second timer is finished, you’ll notice that the tooltip never appears.

Update lifecycle

The update lifecycle methods are made up of several update methods and the render method, which you can see in the listing. With the exception of the render method, these methods never run on the server (so the accessing window and document are safe):

  • componentWillReceiveProps(nextProps)Happens when the component is about to receive properties (runs only when an update happens in a parent component)
  • shouldComponentUpdate(nextProps, nextState) -> boolAllows you to optimize the number of render cycles by determining when the component needs to update
  • componentWillUpdate(nextProps, nextState)Happens right before the component renders
  • render()Renders the component
  • componentDidUpdate(prevProps, prevState)Happens right after the component renders[2]

    2

    Update lifecycle based on Azat Mardan’s React Quickly (Manning, 2017).

Tip

Remember, the mounting lifecycle will always run before any of these methods.

Unmounting event

One final improvement you need to make to the timer is to make sure it gets cleaned up if the user navigates away from the Products page before the timer finishes running. If you don’t do that, you’ll see a React error in the console after 10 seconds. The error explains that the code being run is trying to reference a component that’s no longer mounted in the DOM. This happened because you navigated away from the component the timer was in without turning off the timer. Figure 4.7 is a screenshot of the error.

Figure 4.7. If a component is unmounted, but listeners or timers aren’t cleaned up, they’ll end up with a reference to a null component.

The following listing shows how to add the time-out cleanup to your componentWillUnmount lifecycle function.

Listing 4.12. Cleaning up the timer—src/components/products.jsx
class Products extends React.Component {

  componentWillUpdate(nextProps, nextState) {}

  componentWillUnmount() {
    clearTimeout(this.clearTimer);            1
  }

  updateSearchQuery() {}
}

  • 1 Clear timer on unmount.

There’s only one unmount event: componentWillUnmount(). You can take advantage of this event to clean up any manually attached event listeners and shut down any timers you may have running. This method runs only in the browser. To see all the code for the chapter, you can check out branch chapter-4-complete (git checkout chapter-4-complete).

Now that you understand the React lifecycle, let’s explore component architecture patterns that can help you build great React apps.

4.3. Component patterns

You can compose React components in user interfaces in two well-defined ways:

  • Higher-order components
  • Presentation and container components

In the All Things Westies app, it’s beneficial to create reusable parts for the view and business logic. This has long-term maintainability benefits for developers and makes your code easier to reason about.

In some cases, you add reusability by creating a component that takes in another component and extends its functionality—a decorator. This happens in Redux when you wrap a view component with the Connect component. In other cases, you split your components into two types: components that focus on business logic and components that focus on what the app looks like. For example, the Products component focuses on the business logic of the view.

4.3.1. Higher-order components

When building a modular, component-driven UI, you end up having a lot of components that need the same kind of data fetching or that have the same view with different data fetching. For example, you may have many views that use user data in some way. Or you may have many views that use a List component but with different data sets. In these cases, you want a way to pull out the data-fetching and manipulation logic, making it separate from the component that displays the data.

Even though you haven’t added any data fetching to the All Things Westies app yet, you’ll eventually need to do that. The products view will need to know about the products available for sale. Imagine you wanted to make a component that knew how to fetch all the products. It’d look something like this:

const ProductsDataFetcher = (Component) => {
  ... // fetches the products data
  ... // ensures data is compatible with the products component
  return <Component data={this.state.data} />
}

The most important part of this example function is that you pass in the component (the Products component in this example) to the ProductsDataFetcher function. In this case, the higher-order component (HOC) function knows how to get the product data and will then pass that data into the component (Figure 4.8). This abstracts away any state or logic from the Products View component, leaving it to focus on the UI concerns.

Figure 4.8. Higher-order functions take a function and return a new function with additional functionality. (Reproduced from Azat Mardan’s React Quickly, Manning, 2017.

If you have a component and then pass it into the higher-order component, you’ll end up with the original component plus additional functionality. In React, this almost always results in offloading some sort of state management to the parent HOC. In the ListDataFetcher example, the HOC knows about the app state and fetching the data. That allows the List component to be a presentation component that’s highly reusable.

4.3.2. Component types: presentation and container

It’s possible to categorize React components into two distinct buckets: presenters and containers. By following this binary type pattern, you can maximize your code reuse and minimize unnecessary code coupling and complexity.

Earlier in this chapter, you built the Products page of the All Things Westies app. This has a component called Products that holds onto the state for its part of the application. Later in the book, it’ll also be responsible for managing data fetching via Redux. These responsibilities make it a container component.

On the other hand, the Item and App components are presentation components. Both contain display elements and rely on properties to determine their functionality. Presentation components determine how the app looks.

Table 4.1 lists the value of container and presentation components.

Table 4.1. Attributes of component types

Container

Presentation

Contains state Limited state (for user interactions), ideally implemented as a functional component
Responsible for how the app works Responsible for how the app looks
Children: container and presentation components Children: container and presentation components
Connect to rest of application (for example, Redux) No dependencies on model or controller portions of the app (for example, Redux)

Container components abstract state away from their children. They also handle layout and are generally responsible for the how of the application. Some higher-order components have this as their main purpose. They listen for data changes and then pass that state down as properties. Redux provides a higher-order component that helps with this (see chapter 6).

Presentation components contain only state related to user interactions. Whenever possible, they should be implemented as pure components. They’re concerned with what the application looks like.

One important note is that containers can have other containers and presentation components as children. Conversely, presentation components can have both containers and presentation components as children. These two types of component nesting should be kept flexible to maximize code composition. That may feel strange at first, but keeping the two component types clear will help you in the long run.

Summary

In this chapter, you learned how to set up and use React Router to have a complete single-page app experience. You also learned more about React by exploring the component lifecycle. Finally, you learned key patterns that are commonly used when building React apps.

  • React Router uses React’s concepts of components to compose routes into any React app.
  • React Router abstracts the history object and provides utilities for linking.
  • React Router has routing hooks that let you add advanced logic.
  • React lifecycle methods are used as hooks into the render cycle.
  • The initial render cycle can be used to trigger timers or lock down logged-in routes.
  • Many component patterns are available for composing React components in reusable and maintainable ways.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.103.5