Managing the Internal State

Components have the ability to store data that can change over time.

When a component shows data that can change over time, we want changes to be shown as soon as possible. For example, consider the ProductList component: it shows a list of products contained in the products array. If a new product is added to the array, we want it to be shown immediately. React provides a mechanism to support the automatic rendering of a component when data changes. Such a mechanism is based on the concept of state.

React state is a property that represents data that changes over time. Every component supports the state property, but it should be used carefully.

Again, consider the ProductList component:

import React from 'react';
import Product from './Product';

class ProductList extends React.Component {
render() {
let products = [
{code:"P01", name: "Traditional Merlot", description: "A bottle
of middle weight wine, lower in tannins (smoother), with a more
red-fruited flavor profile."},
{code:"P02", name: "Classic Chianti", description: "A medium-bodied
wine characterized by a marvelous freshness with a lingering,
fruity finish"},
{code:"P03", name: "Chardonnay", description: "A dry full-bodied
white wine with spicy, bourbon-y notes in an elegant bottle"},
{code:"P04", name: "Brunello di Montalcino", description: "A bottle
of red wine with exceptionally bold fruit flavors, high tannin,
and high acidity"}
];

let productComponents = [];

for (let product of products) {
productComponents.push(<Product item={product}/>);
}

return <ul>{productComponents}</ul>;
}
}

export default ProductList;

From a practical point of view, it is not so useful. It shows a hardcoded list of products. If we want to add a new product, we need to make changes to the component source code.

In a real-world scenario, we want to keep the component's code independent from the product data. For example, we would get product data by making an HTTP request to the web server. In this case, the products array would represent data that changes over time: initially an empty array, it would then be filled with product data received from the server, and it could be changed again by subsequent requests to the server.

Components that store data that can change over time are said to be stateful components. A stateful component stores the state in the this.state property. To inform a component that the state has changed, you must use the setState() method. This method sets a new state for the component; it does not update it. Changes to the state trigger the component's rendering; that is, the automatic execution of the render() method.

Let's see how to manage the state by changing the ProductList component definition:

import React from 'react';
import Product from './Product';

class ProductList extends React.Component {
constructor() {
super();
this.state = { products: [] };

fetch("products.json")
.then(response => response.json())
.then(json => {this.setState({products: json})})
.catch(error => console.log(error));
}

render() {
let productComponents = [];

for (let product of this.state.products) {
productComponents.push(<Product item={product}/>);
}
return <ul>{productComponents}</ul>;
}
}
export default ProductList;

We added the constructor to the component. The constructor runs the superclass constructor and sets the initial state of the component to an object with the products property as an empty array.

Then, send a GET HTTP request to the server via fetch(). Since the request is asynchronous, the initial rendering of the component will be an empty list of products.


State initialization is the only case where you can assign a value to the this.state property without using setState().

When the HTTP response is received, it is used to change the component's state with setState(). This state change causes the automatic execution of render(), which will show the list of products received from the server.

Now that we know how to manage the component's state, here are a couple of things to remember when using the setState() method:

  • setState() merges new data with old data already contained in the state, and overwrites the previous state
  • setState() triggers the execution of the render() method, so you should never call render() explicitly

Component state management appears to be very simple. However, it is easy to get in trouble when deciding what should be considered state and which component should be stateful.

Here is some advice about state:

  • State should contain the minimum data needed to represent data that can change over time in your UI; any information that can be derived from this minimal data should be computed inside the render() method
  • State should be avoided as much as possible, since it adds complexity to a component
  • Stateful components should be located high up in the component hierarchy of a UI

We can consider the last piece of advice a consequence of the second piece of advice. If we should restrict the usage of state, we should reduce the number of stateful components. So, it is a good rule to assign the role of stateful component to components that are the root of a component hierarchy in a user interface. Do you remember the classification of components into presentational and container components, which we discussed in the previous chapter? In general, container components are good candidates for stateful components.

In our example application, we assigned the role of a stateful component to the ProductList component. Even if it is a container component, it is not the highest in the component hierarchy of the application. Maybe this role would be more appropriate for the Catalog component. In this case, we should move the logic of getting data inside of the Catalog component, as shown in the following code:

import React from 'react';
import './Catalog.css';
import ProductList from './ProductList';

class Catalog extends React.Component {
constructor() {
super();
this.state = { products: [] };

fetch("products.json")
.then(response => response.json())
.then(json => {this.setState({products: json})})
.catch(error => console.log(error));
}

render() {
return <div><h2>Wine Catalog</h2><ProductList
items={this.state.products}/></div>;
}
}

export default Catalog;

You can find a project ready in the my-shop-05 folder at Code/Chapter-2.
..................Content has been hidden....................

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