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>
... ... ...
... ... ...
... ... ...
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 ]
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>
... ... ...
... ... ...
... ... ...
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;
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>
... ... ...
... ... ...
... ... ...
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>
... ... ...
... ... ...
... ... ...
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>
... ... ...
... ... ...
... ... ...
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;
Alternatively, without function declaration, arrayFunc
— anonymous function:
... ... ...
... ... ...
... ... ...
<div>
{
state.persons.map((person, personIndex) => {
return <Person
name={person.name}
language={person.language}
id={person.id}
key={personIndex} />
};)
}
</div>
... ... ...
... ... ...
... ... ...
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.
Top comments (0)