Looping through List components in React
Tags: react, tutorial, javascript, codenewbie
Published: 2021-01-08 22:44:20

###### *This article is sponsored by [Fiverr](http://www.fiverr.com/s2/fa85266916) - Freelance to get extra income or become a full-time freelancer.*

In the previous article, we saw how to output content dynamically. There's one issue we need to tackle though. That is manually extracting the person's array (`persons = [ ... ]`) from the state object (`const state = { ... }`) to each component as attributes (`<Person ... />`) by zero indexing.

*Person's array in the state object*:

**App.js**
```jsx
const [state, setState] = useState({
    persons: [
      { name: 'Bello', language: 'React', id: '2sdr3' },
      { name: 'Michael', language: 'Vue', id: 'de6c3' },
      { name: 'Mary', language: 'Angular', id: 'c1z3x' }
    ],
    showPersons: true
 });
                         ...    ...    ...
                         ...    ...    ...
                         ...    ...    ...
<div>
    <Person
        name={state.persons[0].name}
        language={state.persons[0].language}
        id={state.persons[0].id} />
    <Person
        name={state.persons[1].name}
        language={state.persons[1].language}
        id={state.persons[1].id} />
    <Person
        name={state.persons[2].name}
        language={state.persons[2].language}
        id={state.persons[2].id} />
</div>
                         ...    ...    ...
                         ...    ...    ...
                         ...    ...    ...
```

The *Zero indexing* makes components difficult to maintain, especially for complex apps.

The solution is to use a more elegant approach like **ES6+ mapping of arrays**.

For example:

**JS**
```js
const array = [ 1, 4, 5, 2, 7 ];

const arrayFunc = element => {
    return element * 2;
};

array.map(arrayFunc);

// output: [ 2, 8, 10, 4, 14 ]
```  

> Array mapping executes each member or element in an array to perform equal operations.

An effective way is to loop through each component in an array by mapping (**for-loop** or any other approach).

```jsx
                         ...    ...    ...
                         ...    ...    ...
                         ...    ...    ...
<div>
    {
      state.persons.map(person => {
        return <Person
          name={person.name}
          language={person.language}
          id={person.id} />
      })
    }
</div>
                         ...    ...    ...
                         ...    ...    ...
                         ...    ...    ...
```

The overall code:

```jsx
import React from 'react';
import { useState } from 'react';
import Person from './Person/Person';
import bodyStyles from './body.module.css';

const App = () => {
  const [state, setState] = useState({
    persons: [
      { name: 'Bello', language: 'React', id: '2sdr3' },
      { name: 'Michael', language: 'Vue', id: 'de6c3' },
      { name: 'Mary', language: 'Angular', id: 'c1z3x' }
    ],
    showPersons: true
  });

  const personsToggleHandler = () => {
    const showCards = state.showPersons;
    setState((prevState) => ({
      ...prevState,
      showPersons: !showCards
    }))
  }

  let personsList = state.showPersons ?
    (
      <div>
        {
          state.persons.map(person => {
            return <Person
              name={person.name}
              language={person.language}
              id={person.id} />
          })
        }
      </div>
    ) : null;

  return (
    <div className={bodyStyles.body}>
      <div className='Person-Container'>
        {personsList}
        <h3
          style={{
            textAlign: 'center',
            fontFamily: 'sans-serif'
          }}>Toggle Persons</h3>
      </div>

      <div className={bodyStyles.btns}>
        <button
          className={bodyStyles.button}
          onClick={personsToggleHandler}>Toggle Person Cards</button>
      </div>
    </div>
  );
};

export default App;
```

Solving the issue above led to a new bug to tackle in the browser console.

Open the browser console with the keyboard shortcut, `F12`, notice the warning that says, "*Warning: Each child in a list should have a unique **key** prop*." That means each person member in the array of the state object must have a `key` to link to each component list attribute.

> The index of each element in a component list must have its key.

**App.js**

```jsx
const [state, setState] = useState({
    persons: [
      { name: 'Bello', language: 'React', id: '2sdr3' },
      { name: 'Michael', language: 'Vue', id: 'de6c3' },
      { name: 'Mary', language: 'Angular', id: 'c1z3x' }
    ],
    showPersons: true
 });
                         ...    ...    ...
                         ...    ...    ...
                         ...    ...    ...
<div>
    {
      state.persons.map((person, personIndex) => {
        return <Person
          name={person.name}
          language={person.language}
          id={person.id}
          key={personIndex} />
      })
    }
</div>
                         ...    ...    ...
                         ...    ...    ...
                         ...    ...    ...
```

Now, there's a new property, `personIndex` mapping each element in the `persons` array of the `state` object to the component list attributes. You will find no error in the console again.

> The `key` is a reserved keyword in JavaScript which acts as an agent to maps each element in the array to component list attributes.

You may wish to omit the `personIndex` property but use the unique `id` property for each person's array object to map each component's property.

```jsx
const [state, setState] = useState({
    persons: [
      { name: 'Bello', language: 'React', id: '2sdr3' },
      { name: 'Michael', language: 'Vue', id: 'de6c3' },
      { name: 'Mary', language: 'Angular', id: 'c1z3x' }
    ],
    showPersons: true
 });
                         ...    ...    ...
                         ...    ...    ...
                         ...    ...    ...
<div>
  {
    state.persons.map(person => {
      return <Person
        name={person.name}
        language={person.language}
        id={person.id}
        key={person.id} />
    })
  }
</div>
                         ...    ...    ...
                         ...    ...    ...
                         ...    ...    ...
```

### Why Use Key?
The *key* attribute updates the component list effectively because it uses the **Virtual DOM** to know the specific state that changes. This prevents re-rending the full list whenever a little change is made in a component. That's why we had that warning, informing us to use the **key** property to manage the state effectively.

In JSX, mapping an array (person's array) to an array function containing the component list is shown below:

**JSX**
```jsx
                         ...    ...    ...
                         ...    ...    ...
                         ...    ...    ...
let arrayFunc;
                         ...    ...    ...
<div>
  {
    arrayFunc = (person, personIndex) => {
      return <Person
        name={person.name}
        language={person.language}
        id={person.id}
        key={personIndex} />
    }
  }
  {state.persons.map(arrayFunc)};
</div>
                         ...    ...    ...
                         ...    ...    ...
                         ...    ...    ...
```

Although, the `arrayFunc` function needs to be declared outside of the `<div>`.

The overall code:

```jsx
import React from 'react';
import { useState } from 'react';
import Person from './Person/Person';
import bodyStyles from './body.module.css';

const App = () => {
  const [state, setState] = useState({
    persons: [
      { name: 'Bello', language: 'React', id: '2sdr3' },
      { name: 'Michael', language: 'Vue', id: 'de6c3' },
      { name: 'Mary', language: 'Angular', id: 'c1z3x' }
    ],
    showPersons: true
  });

  const personsToggleHandler = () => {
    const showCards = state.showPersons;
    setState((prevState) => ({
      ...prevState,
      showPersons: !showCards
    }))
  }

  let arrayFunc;
  let personsList = state.showPersons ?
    (
      <div>
        {
          arrayFunc = (person, personIndex) => {
            return <Person
              name={person.name}
              language={person.language}
              id={person.id}
              key={personIndex} />
          }
        }
        {state.persons.map(arrayFunc)};
      </div>
    ) : null;

  return (
    <div className={bodyStyles.body}>
      <div className='Person-Container'>
        {personsList}
        <h3
          style={{
            textAlign: 'center',
            fontFamily: 'sans-serif'
          }}>Toggle Persons</h3>
      </div>

      <div className={bodyStyles.btns}>
        <button
          className={bodyStyles.button}
          onClick={personsToggleHandler}>Toggle Person Cards</button>
      </div>
    </div>
  );
};

export default App;
```

Alternatively, without function declaration, `arrayFunc` — **anonymous function**:

```jsx
                         ...    ...    ...
                         ...    ...    ...
                         ...    ...    ...
<div>
  {
    state.persons.map((person, personIndex) => {
      return <Person
        name={person.name}
        language={person.language}
        id={person.id}
        key={personIndex} />
    };)
  }
</div>
                         ...    ...    ...
                         ...    ...    ...
                         ...    ...    ...
```

{% stackblitz react-az2amv %}

{% user bello %}
#### Techstack | Fiverr
Techstack article, sponsored by Fiverr.
- Connect to freelancers with proven business experience.
- Get matched with the perfect talent by a customer service manager.
- Freelance to get extra income or become a full-time freelancer.

[Sign up](http://www.fiverr.com/s2/fa85266916) to find the perfect freelance services for your business or become a freelancer.

{% youtube EkPDLVqKkLc %}

[Support](https://dashboard.flutterwave.com/donate/3lo9klliqwbu) what I do and push me to keep making free content.