J'ai un tableau qui contient des objets. Je crée une carte de ce tableau pour restituer les noms avec un composant span
.
let data = [{"id": "01", "name": "Hi"}, {"id": "02", "name": "Hello"}];
J'ai utilisé les deux fonctionnalités ci-dessous pour itérer sur ce tableau d'objets, et utiliser map pour rendre des éléments JSX.
import React, { Component } from 'react';
class App extends Component {
render() {
let data = [{"id": "01", "name": "Hi"}, {"id": "02", "name": "Hello"}];
const items = data.map((key, i) => {
return <span key={key.id}>{key.name}</span>;
});
return (
<div>
{items}
</div>
);
}
}
export default App;
import React, { Component } from 'react';
class App extends Component {
render() {
let data = [{"id": "01", "name": "Hi"}, {"id": "02", "name": "Hello"}];
let rows = [];
data.map((key, i) => {
rows.Push(<span key={key.id}>{key.name}</span>);
});
return (
<div>
{rows}
</div>
);
}
}
export default App;
J'ai connu ci-dessus deux manières différentes d'utiliser map
et de rendre des éléments JSX. Y a-t-il d'autres façons de faire la même chose, à part ces deux-là? Si oui, lequel est recommandé?
Généralement, je suis cette règle:
Créer un composant qui rend les éléments
// in some file
export const RenderItems = ({data}) => {
return data && data.map((d, i) => <span key={d.id}>{d.name}</span>) || null
}
Accrochez les RenderItems
import { RenderItems } from 'some-file'
class App extends Component {
render() {
// you may also define data here instead of getting data from props
const { data } = this.props
return (
<div>
<RenderItems data={data} />
</div>
)
}
}
Joindre les données dans le composant
const data = [{"id": "01", "name": "Hi"}, {"id": "02", "name": "Hello"}]
<App data={data} />
Le respect de cette règle n'aura aucune incidence sur les performances, même avec votre deuxième exemple de code, à savoir. pousser des éléments dans un tableau et rendre les éléments. Parce que vous ne travaillez pas directement à l'intérieur du crochet de rendu. Veillez toujours à ce que render ne mette en œuvre aucune logique à l'intérieur.
De plus, je ne créerais pas id
juste pour utiliser la clé:
const data = [{"name": "Hi"}, {"name": "Hello"}]
//... and when using index as key
.map((d, i) => <span key={'item-'+i}>
// or,
.map((d, i) => <span key={'item-'+i+'-'+d.name}>
Voir cet article pourquoi je suis cette syntaxe lorsque j'utilise l'index comme clé.
Si vous souhaitez éviter l'utilisation de balises HTML inutiles, vous pouvez utiliser React.Fragment
export const RenderItems = ({data}) => {
return data &&
data.map(
(d, i) => <React.Fragment key={d.id}>{d.name}</React.Fragment>
) || null
}
// and when rendering, you just return the component
return <RenderItems data={data} />
Remarque:
<></>
comme alias pour <React.Fragment></React.Fragment>
uniquement si vous ne possédez aucune propriété supplémentaire. Puisque nous utilisons la propriété de clé dessus, nous ne l'utilisons pas.React.Fragment
.Exemple utilisant <></>
:
<>{d.name}</>
Cela sera rendu la valeur de d.name
en HTML sans balise. Ceci est considéré comme optimal lorsque nous transformons spécifiquement notre conception existante en application réactive. Ou, il pourrait y avoir d'autres cas. Comme, nous allons afficher une liste de définitions:
<dl>
<dt></dt>
<dd></dd>
<dt></dt>
<dd></dd>
<dt></dd>
</dl>
Et nous ne voulons pas attacher de balises html inutiles, alors utiliser Fragment nous facilitera la vie:
Exemple:
<>
<dt>{d.term}</dt>
<dd>{d.definition}</dd>
</>
Le cas le plus important concerne le rendu de l'élément td
dans tr
(un composant TR). Si nous ne le faisons pas, nous enfreignons la règle du HTML. Le composant ne sera pas rendu correctement. En réaction, cela vous jettera une erreur.
Aussi, si vous avez une longue liste d'accessoires comme ci-dessous:
const {
items,
id,
imdbID,
title,
poster,
getMovieInfo,
addToFavorites,
isOpen,
toggleModal,
closeModal,
modalData,
} = props
Vous pouvez envisager une déstructuration comme:
const { items, ...other } = props
// and in your component you can use like:
<div modalData={other.modalData}>
Mais personnellement, je préfère utiliser le premier exemple de code. En effet, lors du développement, je n'ai pas besoin de revenir en arrière sur un autre composant ni de rechercher la console à chaque fois. Dans l'exemple donné, il y a une clé comme modalData={}
, donc nous maintenons facilement modalData={other.modalData}
. Mais que se passe-t-il s'il est nécessaire de coder comme <div>{modalData}</div>
? Ensuite, vous pouvez également accepter ma préférence.
Je ferais ça
const data = [{id: 1, name: 'a'}, {id: 2, name: 'b'}];
export default class App extends PureComponent {
render() {
return (
<div>
{data.map(({ id, name }) => <span key={id}>{name}</span>)}
</div>
);
}
}
Désormais, votre data
n'est pas réinitialisé à chaque rendu, et vous n'avez pas à récupérer des déclarations de variable inutiles.
Le premier moyen est meilleur.
Array.prototype.map
crée un tableau dans les coulisses et le renvoie après application de la modification sur chaque élément. Functionality-1 crée deux tableaux, tandis que Functionality-2 en crée trois.
Functionality-1 se lit mieux. C'est comme ça que le code de React est écrit. Pour un élément simple comme celui-ci, je sauvegarderais la définition const pour les éléments et placerais l'instruction map dans le JSX à renvoyer directement.
En règle générale, l'instruction for
ou while
est le moyen le plus efficace d'itérer un tableau. La façon dont un petit tableau est traité dans un endroit non critique peut être considérée comme une microoptimisation.
L'utilisation de map
est idiomatique dans les composants React, car elle est assez rapide et peut renvoyer une valeur dans le cadre d'une expression.
Bien que ce soit un anti-modèle:
let rows = [];
data.map((key, i) => {
rows.Push(<span key={key.id}>{key.name}</span>);
});
map
est supposé mapper les éléments d'un tableau avec d'autres valeurs (d'où le nom), pas pour itérer un tableau au lieu de forEach
ou d'une autre boucle. Ce problème peut être suivi avec ESLint array-callback-return
rule.
Le composant est sans état et n'a pas besoin d'être une classe Component
. Ce peut être un composant fonctionnel ou une classe PureComponent
. Puisque data
est constant, il n'est pas nécessaire de l'attribuer à chaque rendu:
const data = [{"id": "01", "name": "Hi"}, {"id": "02", "name": "Hello"}];
const App = props => <div>
{data.map(({ id, name }) => <span key={id}>{name}</span>)}
</div>;
la première méthode est correcte. utilisez la fonction map pour parcourir le tableau.
export default class App extends React.Component{
constructor(props){
super(props);
this.state = {
data: [{"id": "01", "name": "Hi"}, {"id": "02", "name": "Hello"}];
};
}
render(){
return(
<div>
{this.state.data.map((data,index)=>
<span key={data.id}>{data.name}</span>
)}
);
}
}
Vous pouvez utiliser ceci pour une meilleure compréhension
const data = [{id: 1, name: 'a'}, {id: 2, name: 'b'}];
export default class App extends React.Component {
render() {
return (
<div>
{data.map((data, dataIndex ) => <span key={dataIndex}>{data.name}</span>)}
</div>
);
}
}
Ici, vous pouvez comprendre que le nom appartient à attribut.
J'irais avec la map
dans le return(...)
car la carte retourne un tableau. C'est plus propre et lisible, ce à quoi je m'efforce personnellement.
Pour ajouter à la réponse, si le tableau data
pouvait changer dans la file, je créerais un nouveau composant de vidage sans état:
const Spanner = props => { return <span key={ props.data.id }>{ props.data.name }</span> };
class App extends Component {
render() {
let data = [{"id": "01", "name": "Hi"}, {"id": "02", "name": "Hello"}];
return (
<div>
{
data.map( (item, ind) => {
return (<Spanner key={item.id} data={item.name} />);
})
}
</div>
);
}
}
}
De cette façon, nous pouvons utiliser ce composant clé comme un composant commun partagé entre différents composants. Et au cas où la data
change avec le temps (ce qui est généralement le cas), vous pouvez écrire une fonction wrapper dans le composant Spanner et appeler la nouvelle fonction si nécessaire .data=[{"id": "01", "name": "Hi", "userType": "Admin"}, {"id": "02", "name": "Hello", "userType": "Client"}];
const UserWidget = (props) => {
return (
<div>
<h4>{ props.type }</h4>
<Spanner key={ props.key } data={ props.name }/>
</div>
);
}
// now both the UserWidget and the Spanner Components can be used wherever required
// say a dashboard Component wants to use the UserWidget
class Dashboard extends Component {
render() {
let data = [{"id": "01", "name": "Hi", "userType": "Admin"}, {"id": "02", "name": "Hello", "userType": "Client"}];
return (
<div>
{
data.map( (item, ind) => {
return (<UserWidget type={ item.userType } key={item.id} data={item.name} />);
})
}
</div>
);
}
}
}
De cette façon, vous ne vous répétez pas et votre composant d'application a toujours le comportement attendu même si le nouveau tableau data
possède désormais la nouvelle clé userType
. C'est encore ce que je ferais.
La fonction toSpan
peut être réutilisée.
class App extends React.Component {
render() {
const data = [{id: `01`, name: `Hi`}, {id: `02`, name: `Hello`}]
const toSpan = ({id, name}) => <span key={id}>{name}</span>
return (
<div>{data.map(toSpan)}</div>
)
}
}