State management is a vital concept to understand to develop a dynamic UI component. A React component can be categorized as either stateful or stateless. A class component uses a state object to update the UI by using a setstate
function that can take updated values or a function to trigger a rendering cycle with the updated data. On the other hand, functional components use React hooks to track data changes of UI updates.
Arrays are often used as a data source to create complex UI elements such as tables, lists, or grids. A state object can store arrays that can be updated with a response from an API or users. React tracks the changes in a state object using a shallow comparison that does not compare attributes of objects while comparing objects.
This guide explains the steps to modify the state of a component using an array.
An array can be traversed using a conventional for
loop or map
function. The content of an array can be traversed and transformed into UI elements using the map
function, which will apply the passed transformer function to each array element and return a list of elements. This can also be done directly in a JSX scope ('{}') to directly render elements on a component.
Follow the below example to implement a list of users with an add button to display input value in the list:
1import React, { Component } from "react";
2import "./style.css";
3
4export default class App extends Component {
5 state = {
6 cart: ["Corn", "Potato"],
7 };
8
9 saveInput = (e) => {
10 this.setState({ input: e.target.value });
11 };
12
13 addNewItem = () => {
14 let { cart, input } = this.state;
15 cart.push(input);
16 // this.state.cart.push(this.state.input); // same as above, though bad practice
17 };
18
19 render() {
20 return (
21 <div>
22 <input
23 type="text"
24 onChange={this.saveInput}
25 />
26 <button onClick={this.addNewItem}> Add Item </button>
27 <ol>
28 {this.state.cart.map((subItems, sIndex) => {
29 return <li key={sIndex}> {subItems}</li>
30 })}
31 </ol>
32 </div>
33 );
34 }
35}
The state
object contains a cart
array with two values. The saveInput
method saves the input from the user in the state object with an input
key label.
On button click event, the cart
and the input
value are retrieved using the destructuring syntax, and the input value is added in the cart array. This is a bad approach because React will never know about the changes in the state unless the setState
method is used.
Note: In the above example, the list is updated as soon as you start typing because
onChange
updates thestate
that re-renders theApp
component.
In order to update the array in the state object, a new array object must be supplied to the input
key using the setState
method:
1addNewItem = () => {
2 let { cart, input } = this.state;
3 cart.push(input);
4 this.setState({cart: cart});
5};
The setState
method will replace the existing array with the updated array, though this can also be achieved with a shorter syntax using the spread (...
) operator:
1addNewItem = () => {
2 this.setState({cart: [...this.state.cart, this.state.input]});
3};
Or it can also be done using the concat
method:
1addNewItem = () => {
2 this.setState({cart: this.state.cart.concat(this.state.input)});
3};
The concat
method creates and returns a new array by combining the cart
array and the input
value.
It is critical to assure the integrity of the state when handing bulky or complex objects. The setState
method is asynchronous, which means the data in the state
object will not be updated instantly, and React can combine multiple state change calls for performance gain. The data can also be updated anytime, so to avoid possible side effects of asynchronous behavior, it is recommended to use the snapshot of the previous state to create a new state:
1addNewItem = () => {
2 this.setState((prevState, props) => ({
3 cart: [...prevState.cart, prevState.input],
4 }));
5};
And the props
can also be skipped to only use prevState
:
1addNewItem = () => {
2 this.setState(prevState => ({
3 cart: [...prevState.cart, prevState.input],
4 }));
5};
• React uses a key to track changes in the dynamic elements (li
) so the index
and element data can be used to make a unique key:
1{
2 this.state.cart.map((subItems, sIndex) => {
3 return <li key={`${subItems}${sIndex}`}> {subItems}</li>;
4 })
5}
• Avoid using consecutive setState
calls. Instead, update values in one go with setState({ foo: value1, bar: value });
.
Immutability is an important principle in React to assure data integrity and consistency in an app. Use the spread operator and previous state to generate a new state and ensure the updated value in the state object. The complete optimized code of App.js
is available in this Github gist file. Happy coding!