Author avatar

Pavneet Singh

Add Data into an Array in a State Object

Pavneet Singh

  • Nov 18, 2020
  • 5 Min read
  • 85 Views
  • Nov 18, 2020
  • 5 Min read
  • 85 Views
Web Development
Front End Web Development
Client-side Frameworks
React

Introduction

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.

Display Array Data in a Component

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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import React, { Component } from "react";
import "./style.css";

export default class App extends Component {
  state = {
    cart: ["Corn", "Potato"],
  };

  saveInput = (e) => {
    this.setState({ input: e.target.value });
  };

  addNewItem = () => {
    let { cart, input } = this.state;
    cart.push(input);
    // this.state.cart.push(this.state.input); // same as above, though bad practice 
  };

  render() {
    return (
      <div>
        <input
          type="text"
          onChange={this.saveInput}
        />
        <button onClick={this.addNewItem}> Add Item </button>
        <ol>
          {this.state.cart.map((subItems, sIndex) => {
            return <li key={sIndex}> {subItems}</li>
          })}
        </ol>
      </div>
    );
  }
}
JSX

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 the state that re-renders the App component.

Display Updated Array Data

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:

1
2
3
4
5
addNewItem = () => {
  let { cart, input } = this.state;
  cart.push(input);
  this.setState({cart: cart});
};
JSX

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:

1
2
3
addNewItem = () => {
  this.setState({cart: [...this.state.cart, this.state.input]});
};
JSX

Or it can also be done using the concat method:

1
2
3
addNewItem = () => {
  this.setState({cart: this.state.cart.concat(this.state.input)});
};
JSX

The concat method creates and returns a new array by combining the cart array and the input value.

Handle Asynchronous Changes in the State

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:

1
2
3
4
5
addNewItem = () => {
  this.setState((prevState, props) => ({
    cart: [...prevState.cart, prevState.input],
  }));
};
JSX

And the props can also be skipped to only use prevState:

1
2
3
4
5
addNewItem = () => {
  this.setState(prevState => ({
    cart: [...prevState.cart, prevState.input],
  }));
};
JSX

Tips

• 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
3
4
5
{
  this.state.cart.map((subItems, sIndex) => {
  return <li key={`${subItems}${sIndex}`}> {subItems}</li>;
  })
}
JSX

• Avoid using consecutive setState calls. Instead, update values in one go with setState({ foo: value1, bar: value });.

Conclusion

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!

1