Loop in React JSX

Ik probeer iets als het volgende te doen in React JSX (waar ObjectRow een afzonderlijk onderdeel is):

<tbody>
    for (var i=0; i < numrows; i++) {
        <ObjectRow/>
    } 
</tbody>

Ik besef en begrijp waarom dit geen geldige JSX is, aangezien JSX verwijst naar functieaanroepen. Omdat ik echter uit templateland kom en nieuw ben in JSX, weet ik niet hoe ik het bovenstaande zou kunnen bereiken (een component meerdere keren toevoegen).


Antwoord 1, autoriteit 100%

Zie het alsof je alleen JavaScript-functies aanroept. U kunt geen for-lus gebruiken waar de argumenten voor een functieaanroep zouden gaan:

return tbody(
    for (var i = 0; i < numrows; i++) {
        ObjectRow()
    } 
)

Zie hoe de functie tbodyeen for-lus als argument wordt doorgegeven, wat leidt tot een syntaxisfout.

Maar je kunt een array maken en dat dan als argument doorgeven:

var rows = [];
for (var i = 0; i < numrows; i++) {
    rows.push(ObjectRow());
}
return tbody(rows);

Je kunt in principe dezelfde structuur gebruiken als je met JSX werkt:

var rows = [];
for (var i = 0; i < numrows; i++) {
    // note: we are adding a key prop here to allow react to uniquely identify each
    // element in this array. see: https://reactjs.org/docs/lists-and-keys.html
    rows.push(<ObjectRow key={i} />);
}
return <tbody>{rows}</tbody>;

Overigens is mijn JavaScript-voorbeeld bijna precies waar dat voorbeeld van JSX in verandert. Speel wat met Babel REPLom een idee te krijgen hoe JSX werkt.


Antwoord 2, autoriteit 66%

Ik weet niet zeker of dit voor jouw situatie werkt, maar vaak kaartis een goed antwoord.

Als dit jouw code was met de forlus:

<tbody>
    for (var i=0; i < objects.length; i++) {
        <ObjectRow obj={objects[i]} key={i}>
    }
</tbody>

Je zou het zo kunnen schrijven met kaart :

<tbody>
    {objects.map(function(object, i){
        return <ObjectRow obj={object} key={i} />;
    })}
</tbody>

ES6-syntaxis:

<tbody>
    {objects.map((object, i) => <ObjectRow obj={object} key={i} />)}
</tbody>

Antwoord 3, autoriteit 36%

Als je nog geen array hebt om map()te gebruiken, zoals het antwoord van @FakeRainBrigand, en dit inline wilt plaatsen zodat de bronlay-out overeenkomt met de uitvoer die dichterbij is dan het antwoord van @SophieAlpert:

Met ES2015 (ES6) syntaxis (spread- en pijlfuncties)

http://plnkr.co/edit/mfqFWODVy8dKQQOkIEGV?p=preview

<tbody>
  {[...Array(10)].map((x, i) =>
    <ObjectRow key={i} />
  )}
</tbody>

Re: transpileren met Babel, de waarschuwingspaginazegt dat Array.fromis vereist voor verspreiding, maar op dit moment (v5.8.23) lijkt dat niet het geval te zijn bij het verspreiden van een daadwerkelijke Array. Ik heb een documentatieprobleemopenstaan om dat te verduidelijken. Maar gebruik op eigen risico of polyfill.

Vanille ES5

Array.apply

<tbody>
  {Array.apply(0, Array(10)).map(function (x, i) {
    return <ObjectRow key={i} />;
  })}
</tbody>

Inline IIFE

http://plnkr.co/edit/4kQjdTzd4w69g8Suu2hT?p=preview

<tbody>
  {(function (rows, i, len) {
    while (++i <= len) {
      rows.push(<ObjectRow key={i} />)
    }
    return rows;
  })([], 0, 10)}
</tbody>

Combinatie van technieken uit andere antwoorden

Houd de lay-out van de bron overeenkomend met de uitvoer, maar maak het inline-gedeelte compacter:

render: function () {
  var rows = [], i = 0, len = 10;
  while (++i <= len) rows.push(i);
  return (
    <tbody>
      {rows.map(function (i) {
        return <ObjectRow key={i} index={i} />;
      })}
    </tbody>
  );
}

Met ES2015-syntaxis & Arraymethoden

Met Array.prototype.fillzou je dit kunnen doen als alternatief voor het gebruik van spread zoals hierboven geïllustreerd:

<tbody>
  {Array(10).fill(1).map((el, i) =>
    <ObjectRow key={i} />
  )}
</tbody>

(Ik denk dat je eigenlijk elk argument voor fill()zou kunnen weglaten, maar daar ben ik niet 100% van overtuigd.) Dank aan @FakeRainBrigand voor het corrigeren van mijn fout in een eerdere versie van de fill()oplossing (zie revisies).

key

In alle gevallen verlicht de keyattr een waarschuwing bij de ontwikkeling, maar is niet toegankelijk voor het kind. U kunt een extra attr doorgeven als u de index beschikbaar wilt hebben in het kind. Zie Lijsten en sleutelsvoor discussie.


Antwoord 4, autoriteit 7%

Gewoon de methode mapArraygebruiken met ES6-syntaxis:

<tbody>
  {items.map(item => <ObjectRow key={item.id} name={item.name} />)} 
</tbody>

Vergeet de eigenschap keyniet.


5, Autoriteit 5%

Als u al gebruikt lodash, de _.timesfunctie is handig.

import React, { Component } from "react";
import Select from "./Select";
import _ from "lodash";
export default class App extends Component {
  render() {
    return (
      <div className="container">
        <ol>
          {_.times(3, (i) => (
            <li key={i}>repeated 3 times</li>
          ))}
        </ol>
      </div>
    );
  }
}

Antwoord 6, autoriteit 3%

Je kunt ook buiten het retourblok uitpakken:

render: function() {
    var rows = [];
    for (var i = 0; i < numrows; i++) {
        rows.push(<ObjectRow key={i}/>);
    } 
    return (<tbody>{rows}</tbody>);
}

Antwoord 7, autoriteit 3%

Misschien wil je afrekenen React Templates, waarmee je JSX-stijl kunt gebruiken templates in React, met een paar instructies (zoals rt-repeat).

Uw voorbeeld, als u reactie-sjablonen zou gebruiken, zou zijn:

<tbody>
     <ObjectRow rt-repeat="obj in objects"/>
</tbody>

Antwoord 8, autoriteit 3%

Er zijn meerdere manieren om dit te doen. JSX wordt uiteindelijk gecompileerd naar JavaScript, dus zolang je geldig JavaScript schrijft, zit je goed.

Mijn antwoord is bedoeld om alle prachtige manieren die hier al zijn gepresenteerd te consolideren:

Als je geen array van objecten hebt, gewoon het aantal rijen:

Binnen het blok returneen Arraymaken en Array.prototype.mapgebruiken:

render() {
  return (
    <tbody>
      {Array(numrows).fill(null).map((value, index) => (
        <ObjectRow key={index}>
      ))}
    </tbody>
  );
}

Buiten het return-blok, gebruik je gewoon een normale JavaScript for-lus:

render() {
  let rows = [];
  for (let i = 0; i < numrows; i++) {
    rows.push(<ObjectRow key={i}/>);
  }
  return (
    <tbody>{rows}</tbody>
  );
}

Onmiddellijk aangeroepen functie-uitdrukking:

render() {
  return (
    <tbody>
      {() => {
        let rows = [];
        for (let i = 0; i < numrows; i++) {
          rows.push(<ObjectRow key={i}/>);
        }
        return rows;
      }}
    </tbody>
  );
}

Als je een reeks objecten hebt

Binnen het returnblok, .map()elk object naar een <ObjectRow>component:

render() {
  return (
    <tbody>
      {objectRows.map((row, index) => (
        <ObjectRow key={index} data={row} />
      ))}
    </tbody>
  );
}

Buiten het return-blok, gebruik je gewoon een normale JavaScript for-lus:

render() {
  let rows = [];
  for (let i = 0; i < objectRows.length; i++) {
    rows.push(<ObjectRow key={i} data={objectRows[i]} />);
  }
  return (
    <tbody>{rows}</tbody>
  );
}

Onmiddellijk aangeroepen functie-uitdrukking:

render() {
  return (
    <tbody>
      {(() => {
        const rows = [];
        for (let i = 0; i < objectRows.length; i++) {
          rows.push(<ObjectRow key={i} data={objectRows[i]} />);
        }
        return rows;
      })()}
    </tbody>
  );
}

Antwoord 9, autoriteit 2%

Als numrowseen array is, is dat heel eenvoudig:

<tbody>
   {numrows.map(item => <ObjectRow />)}
</tbody>

Het array-gegevenstype in React is veel beter. Een array kan een nieuwe array ondersteunen en filteren, verkleinen, enz. ondersteunen.


Antwoord 10, autoriteit 2%

Er zijn verschillende antwoorden die wijzen op het gebruik van de map-instructie. Hier is een compleet voorbeeld waarbij een iterator binnen de component FeatureListwordt gebruikt om Feature-componenten weer te geven op basis van een JSON-gegevensstructuur met de naam features.

const FeatureList = ({ features, onClickFeature, onClickLikes }) => (
  <div className="feature-list">
    {features.map(feature =>
      <Feature
        key={feature.id}
        {...feature}
        onClickFeature={() => onClickFeature(feature.id)}
        onClickLikes={() => onClickLikes(feature.id)}
      />
    )}
  </div>
); 

Je kunt de volledige FeatureList-codeop GitHub. De features-fixture wordt hier weergegeven.


Antwoord 11, autoriteit 2%

Om een ​​aantal keren te lussen en terug te keren, kunt u het bereiken met behulp van fromen map:

<tbody>
  {
    Array.from(Array(i)).map(() => <ObjectRow />)
  }
</tbody>

waar i = number of times


Als u unieke sleutel-ID’s in de gerenderde componenten wilt toewijzen, kunt u React.Children.toArrayzoals voorgesteld in de Bekijk documentatie

react.children.toarray

Retourneert de opaque data-structuur van de kinderen als een platte array met sleutels die aan elk kind zijn toegewezen. Handig als u de collecties van kinderen op uw rendermethoden wilt manipuleren, vooral als u dit wilt opnieuw ordenen of plak. Props.Children voordat u het doorgeeft.

Opmerking:

React.Children.toArray()Wijzigt sleutels om de semantiek van geneste arrays te behouden bij het afvlakken van lijsten van kinderen. Dat wil zeggen, om elke toets in de geretourneerde array te voegen, zodat de toets van elk element wordt gescheurd naar de ingangsarray die het bevat.

<tbody>
  {
    React.Children.toArray(
      Array.from(Array(i)).map(() => <ObjectRow />)
    )
  }
</tbody>

12, Autoriteit 2%

Laat ons zeggen dat we een scala aan items in uw staat hebben:

[{name: "item1", id: 1}, {name: "item2", id: 2}, {name: "item3", id: 3}]
<tbody>
    {this.state.items.map((item) => {
        <ObjectRow key={item.id} name={item.name} />
    })}
</tbody>

Antwoord 13, autoriteit 2%

Als u ervoor kiest om dit te converteren binnen return()van de render-methode, is de eenvoudigste optie om de map( )-methode te gebruiken . Wijs uw array toe aan de JSX-syntaxis met behulp van de functie map(), zoals hieronder weergegeven (ES6-syntaxis wordt gebruikt).


In de bovenliggende component:

<tbody>
   { objectArray.map(object => <ObjectRow key={object.id} object={object.value}>) }
</tbody>

Let op: het kenmerk keyis toegevoegd aan uw onderliggende component. Als u geen sleutelkenmerk heeft opgegeven, ziet u de volgende waarschuwing op uw console.

Waarschuwing: elk kind in een array of iterator zou moeten hebben:
een unieke “sleutel” prop.

Opmerking:Een veelgemaakte fout die mensen maken, is het gebruik van indexals sleutel bij het herhalen. Het gebruik van indexvan het element als sleutel is een antipatroon, en u kunt er meer over lezen hier. Kortom, als het geeneen statische lijst is, gebruik dan nooit indexals sleutel.


Nu hebt u in de component ObjectRowtoegang tot het object vanuit zijn eigenschappen.

In de ObjectRow-component

const { object } = this.props

Of

const object = this.props.object

Hiermee zou u het object moeten ophalen dat u van de bovenliggende component hebt doorgegeven aan de variabele objectin de ObjectRow-component. Nu kunt u de waarden in dat object uitspugen volgens uw doel.


Referenties:

map()-methode in JavaScript

ECMAScript 6 of ES6


Antwoord 14

ES2015 Array.frommet de kaartfunctie + toets

Als je niets hebt aan .map(), kun je Array.from()gebruiken met de functie mapom elementen te herhalen:

<tbody>
  {Array.from({ length: 5 }, (value, key) => <ObjectRow key={key} />)}
</tbody>

Antwoord 15

Ik gebruik dit:

gridItems = this.state.applications.map(app =>
          <ApplicationItem key={app.Id} app={app } />
);

PS: vergeet nooit de sleutel, anders krijg je veel waarschuwingen!


Antwoord 16

Een ECMAScript 2015 / Babelmogelijkheid is het gebruik van een generatorfunctie om een array van JSX:

function* jsxLoop(times, callback)
{
    for(var i = 0; i < times; ++i)
        yield callback(i);
}
...
<tbody>
    {[...jsxLoop(numrows, i =>
        <ObjectRow key={i}/>
    )]}
</tbody>

Antwoord 17

…Of u kunt ook een array van objecten voorbereiden en deze toewijzen aan een functie om de gewenste uitvoer te krijgen. Ik geef hier de voorkeur aan, omdat het me helpt om de goede praktijk van codering te behouden zonder logica in de terugkeer van render.

render() {
const mapItem = [];
for(let i =0;i<item.length;i++) 
  mapItem.push(i);
const singleItem => (item, index) {
 // item the single item in the array 
 // the index of the item in the array
 // can implement any logic here
 return (
  <ObjectRow/>
)
}
  return(
   <tbody>{mapItem.map(singleItem)}</tbody>
  )
}

Antwoord 18

Gebruik gewoon .map()om door uw verzameling te bladeren en <ObjectRow>items terug te sturen met rekwisieten van elke iteratie.

Ervan uitgaande dat objectsergens een array is…

<tbody>
  { objects.map((obj, index) => <ObjectRow obj={ obj } key={ index }/> ) }
</tbody>

Antwoord 19

Dit kan op meerdere manieren.

  1. Zoals hierboven gesuggereerd, sla vóór returnalle elementen op in de array

  2. Loop in return

    Methode 1

    let container =[];
        let arr = [1,2,3] //can be anything array, object 
        arr.forEach((val,index)=>{
          container.push(<div key={index}>
                         val
                         </div>)
            /** 
            * 1. All loop generated elements require a key 
            * 2. only one parent element can be placed in Array
            * e.g. container.push(<div key={index}>
                                        val
                                  </div>
                                  <div>
                                  this will throw error
                                  </div>  
                                )
            **/   
        });
        return (
          <div>
             <div>any things goes here</div>
             <div>{container}</div>
          </div>
        )
    

    Methode 2

      return(
         <div>
         <div>any things goes here</div>
         <div>
            {(()=>{
              let container =[];
              let arr = [1,2,3] //can be anything array, object 
              arr.forEach((val,index)=>{
                container.push(<div key={index}>
                               val
                               </div>)
                             });
                        return container;     
            })()}
         </div>
      </div>
    )
    

Antwoord 20

Ik geef de voorkeur aan een benadering waarbij de programmeerlogica buiten de retourwaarde van rendervalt. Dit helpt om te houden wat eigenlijk is gemaakt, gemakkelijk te grommen.

Dus ik zou waarschijnlijk zoiets doen als:

import _ from 'lodash';
...
const TableBody = ({ objects }) => {
  const objectRows = objects.map(obj => <ObjectRow object={obj} />);      
  return <tbody>{objectRows}</tbody>;
} 

Toegegeven, dit is zo’n kleine hoeveelheid code dat het inlinen prima zou kunnen werken.


Antwoord 21

Je JSX-code wordt gecompileerd tot pure JavaScript-code, alle tags worden vervangen door ReactElement-objecten. In JavaScript kun je een functie niet meerdere keren aanroepen om de geretourneerde variabelen te verzamelen.

Het is illegaal, de enige manier is om een array te gebruiken om de door de functie geretourneerde variabelen op te slaan.

Of u kunt Array.prototype.mapdie beschikbaar is sinds JavaScript ES5om deze situatie aan te pakken.

Misschien kunnen we een andere compiler schrijven om een nieuwe JSX-syntaxis te recreëren om een herhaalfunctie te implementeren, net zoals Angular’s ng-repeat.


Antwoord 22

Je kunt natuurlijk oplossen met een .map zoals voorgesteld door het andere antwoord. Als je Babelal gebruikt, kun je overwegen om jsx-control-statements.

Ze vereisen een beetje instelling, maar ik denk dat het de moeite waard is in termen van leesbaarheid (vooral voor niet-React-ontwikkelaars).
Als u een linter gebruikt, is er ook eslint-plugin-jsx-control- verklaringen.


Antwoord 23

Hier is een eenvoudige oplossing voor.

var Object_rows = [];
for (var i = 0; i < numrows; i++) {
  Object_rows.push(<ObjectRow />);
}
<tbody>{Object_rows}</tbody>;

Er is geen toewijzing en complexe code vereist. Je hoeft alleen maar de rijen naar de array te pushen en de waarden terug te geven om deze weer te geven.


Antwoord 24

U kunt ook een zelfoproepende functie gebruiken:

return <tbody>
           {(() => {
              let row = []
              for (var i = 0; i < numrows; i++) {
                  row.push(<ObjectRow key={i} />)
              }
              return row
           })()}
        </tbody>

Antwoord 25

Ik gebruik het als

<tbody>
  { numrows ? (
     numrows.map(obj => { return <ObjectRow /> }) 
    ) : null
  }
</tbody>

Antwoord 26

Aangezien u JavaScript-syntaxis in JSX-code schrijft, moet u uw JavaScript-code tussen accolades plaatsen.

row = () => {
   var rows = [];
   for (let i = 0; i<numrows; i++) {
       rows.push(<ObjectRow/>);
   }
   return rows;
}
<tbody>
{this.row()}
</tbody>

Antwoord 27

Hier is een voorbeeld uit de React-documentatie, JavaScript-expressies als kinderen:

function Item(props) {
  return <li>{props.message}</li>;
}
function TodoList() {
  const todos = ['finish doc', 'submit pr', 'nag dan to review'];
  return (
    <ul>
      {todos.map((message) => <Item key={message} message={message} />)}
    </ul>
  );
}

Wat uw zaak betreft, stel ik aan om als volgt te schrijven:

function render() {
  return (
    <tbody>
      {numrows.map((roe, index) => <ObjectRow key={index} />)}
    </tbody>
  );
}

Let op de -toets is erg belangrijk, omdat reageer de -toets gebruiken om gegevens in array te verschillen.


28

je kunt iets doen als:

let foo = [1,undefined,3]
{ foo.map(e => !!e ? <Object /> : null )}

29

Met de tijd wordt de taal volwassener en struikelen we vaak op veel voorkomende problemen zoals deze. Het probleem is om een ​​component ‘n’ tijden te gebruiken.

{[...new Array(n)].map((item, index) => <MyComponent key={index} />)}

Waar, n -Is het aantal keren dat u wilt lussen. itemWORDT ONGEWICHT EN indexZAL ZIJN ALS Usual. Ook, eSlint ontmoedigt met behulp van een array-index als toets.

Maar u hebt het voordeel dat u de array niet eerder wilt initialiseren en het meest belangrijk bij het vermijden van de forlus …

Om het ongemak van item als undefined te voorkomen, kunt u een _gebruiken, zodat deze wordt genegeerd bij het steunen en geen voeringfoutgeeft, zoals

{[...new Array(n)].map((_, index) => <MyComponent key={index} />)}

Other episodes