DEV Community

loading...
Cover image for Looping through List components in React

Looping through List components in React

bello profile image Bello Updated on ・4 min read
This article is sponsored by Fiverr - 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

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>
                         ...    ...    ...
                         ...    ...    ...
                         ...    ...    ...
Enter fullscreen mode Exit fullscreen mode

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

const array = [ 1, 4, 5, 2, 7 ];

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

array.map(arrayFunc);

// output: [ 2, 8, 10, 4, 14 ]
Enter fullscreen mode Exit fullscreen mode

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).

                         ...    ...    ...
                         ...    ...    ...
                         ...    ...    ...
<div>
    {
      state.persons.map(person => {
        return <Person
          name={person.name}
          language={person.language}
          id={person.id} />
      })
    }
</div>
                         ...    ...    ...
                         ...    ...    ...
                         ...    ...    ...
Enter fullscreen mode Exit fullscreen mode

The overall code:

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;
Enter fullscreen mode Exit fullscreen mode

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

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>
                         ...    ...    ...
                         ...    ...    ...
                         ...    ...    ...
Enter fullscreen mode Exit fullscreen mode

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.

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>
                         ...    ...    ...
                         ...    ...    ...
                         ...    ...    ...
Enter fullscreen mode Exit fullscreen mode

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

                         ...    ...    ...
                         ...    ...    ...
                         ...    ...    ...
let arrayFunc;
                         ...    ...    ...
<div>
  {
    arrayFunc = (person, personIndex) => {
      return <Person
        name={person.name}
        language={person.language}
        id={person.id}
        key={personIndex} />
    }
  }
  {state.persons.map(arrayFunc)};
</div>
                         ...    ...    ...
                         ...    ...    ...
                         ...    ...    ...
Enter fullscreen mode Exit fullscreen mode

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

The overall code:

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;
Enter fullscreen mode Exit fullscreen mode

Alternatively, without function declaration, arrayFuncanonymous function:

                         ...    ...    ...
                         ...    ...    ...
                         ...    ...    ...
<div>
  {
    state.persons.map((person, personIndex) => {
      return <Person
        name={person.name}
        language={person.language}
        id={person.id}
        key={personIndex} />
    };)
  }
</div>
                         ...    ...    ...
                         ...    ...    ...
                         ...    ...    ...
Enter fullscreen mode Exit fullscreen mode

bello image

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 to find the perfect freelance services for your business or become a freelancer.

Support what I do and push me to keep making free content.

Discussion (0)

pic
Editor guide