Pętla wewnątrz React JSX

1278

Próbuję zrobić coś takiego w React JSX(gdzie ObjectRow jest osobnym składnikiem):

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

Rozumiem i rozumiem, dlaczego to nie jest poprawne JSX, skoro JSXmapy do wywołań funkcji. Jednakże, pochodząc z szablonu i będąc nowym JSX, nie jestem pewien, jak bym to osiągnął (wielokrotne dodawanie komponentu).

Ben Roberts
źródło
38
Należy zauważyć, że w JSX potrzebne są tagi {} wokół składni JavaScript. Może to pomóc facebook.github.io/react/docs/… .
Isaiah Gray
30
let todos = this.props.todos.map((todo) => {return <h1>{todo.title}</h1>})
OverCoder,
@OverCoder dlaczego miałbyś umieścić cały zwrot w tagu {} to byłoby => return <h1> {todo.title} </h1> Czyż nie?
pravin poudel
1
@pravinpoudel właściwie ta odpowiedź jest stara, bardziej jak let todos = this.props.todos.map(t => <h1>{t.title}</h1>):)
OverCoder

Odpowiedzi:

1204

Pomyśl o tym, jakbyś tylko wywoływał funkcje JavaScript. Nie można użyć forpętli, do której trafiłyby argumenty wywołania funkcji:

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

Zobacz, jak funkcja tbodyjest przekazywana do forpętli jako argument, i oczywiście jest to błąd składniowy.

Ale możesz utworzyć tablicę, a następnie przekazać ją jako argument:

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

Podczas pracy z JSX można używać zasadniczo tej samej struktury:

var rows = [];
for (var i = 0; i < numrows; i++) {
    // note: we add 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>;

Nawiasem mówiąc, mój przykład JavaScript jest prawie dokładnie tym, na co przekształca się ten przykład JSX. Baw się z Babel REPL, aby przekonać się, jak działa JSX.

Sophie Alpert
źródło
3
Możliwe, że kompilator się zmienił, ponieważ ten przykład nie wydaje się kompilować w kompilatorze jsx , ale użycie map jak w odpowiedzi FakeRainBrigand poniżej wydaje się poprawnie kompilować.
rav
9
Mogę obiecać, że ostatni przykład nadal kompiluje się poprawnie w najnowszym kompilatorze JSX.
Sophie Alpert,
3
To działa dobrze. Odpowiedź FakeRainBrigand działa dla prostej iteracji tablicy, ale ta odpowiedź jest niezbędna w scenariuszach, w których nie można użyć map(np. Nie masz kolekcji przechowywanej w zmiennej).
Kelvin,
52
React musi efektywnie aktualizować dom, w tym przypadku rodzic powinien przypisać a keydo każdego potomka. ODNIESIENIE: facebook.github.io/react/docs/…
qsun
7
Ten przykład będzie działał, ale brakuje niektórych ważnych bitów, takich jak keywłaściwość, a także styl kodu, który nie jest tak naprawdę używany w bazach kodu React. Proszę uważać tę odpowiedź stackoverflow.com/questions/22876978/loop-inside-react-jsx/… za poprawną.
okonetchnikov
865

Nie jestem pewien, czy to zadziała w twojej sytuacji, ale często mapa jest dobrą odpowiedzią.

Jeśli to był twój kod z pętlą for:

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

Możesz napisać to tak z mapą :

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

Składnia ES6:

<tbody>
    {objects.map((object, i) => <ObjectRow obj={object} key={i} />)}
</tbody>
Bandyta
źródło
43
Mapa ma większy sens, jeśli iterat jest tablicą; Pętla for jest odpowiednia, jeśli jest liczbą.
Zaklinacz kodów
26
<tbody>{objects.map((o, i) => <ObjectRow obj={o} key={i}/>}</tbody>używając wsparcia ES6 Reactify lub Babel.
Distortum
3
+1. Używam tego nawet z ul (lista nieuporządkowana). <ul> {objects.map((object:string,i:number)=>{ return <li>{object}</li> })} </ul>za pomocą TypeScript
Roberto C Navarro
5
to świetnie, ale nie można odwzorować obiektu .. do tego chciałbym użyćfor..in
Damon
4
Pamiętaj, że kolejność kluczy jest dowolna podczas korzystania z Object.keys. Uważam, że osobne przechowywanie kolejności kluczy działa dobrze, a także porządek na literale obiektu, jeśli to konieczne. To pozwala mi pisać kod podobny dothis.props.order.map((k)=><ObjectRow key={k} {...this.props.items[k]} />)
tgrrr
443

Jeśli nie masz jeszcze tablicy, która map()polubiłaby odpowiedź @ FakeRainBrigand, i chcesz to wstawić, aby układ źródłowy odpowiadał wynikowi bliżej niż odpowiedź @ SophieAlpert:

Ze składnią ES2015 (ES6) (funkcje rozkładania i strzałek)

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

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

Re: transpilacja z Babel, jego strona z zastrzeżeniami mówi, że Array.fromjest to wymagane do rozprzestrzeniania, ale obecnie ( v5.8.23) nie wydaje się tak w przypadku rozpowszechniania faktów Array. Mam problem z dokumentacją, aby to wyjaśnić. Ale używaj na własne ryzyko lub wypełniacz.

Vanilla 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>

Połączenie technik z innych odpowiedzi

Zachowaj układ źródłowy odpowiadający wynikowi, ale spraw, aby wstawiana część była bardziej zwarta:

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>
  );
}

Ze składnią ES2015 i Array metodami

Dzięki temu Array.prototype.fillmożesz to zrobić jako alternatywę dla spreadu, jak pokazano powyżej:

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

(Myślę, że faktycznie można pominąć każdy argument fill(), ale nie jestem w 100% tego zdania.) Dzięki @FakeRainBrigand za naprawienie mojego błędu we wcześniejszej wersji fill()rozwiązania (zobacz wersje).

key

We wszystkich przypadkach keyattr łagodzi ostrzeżenie dzięki kompilacji programistycznej, ale nie jest dostępne u dziecka. Możesz przekazać dodatkowe attr, jeśli chcesz, aby indeks był dostępny dla dziecka. Zobacz Listy i klucze do dyskusji.

JMM
źródło
2
Co yield? Pchanie elementów do tablicy pośredniej jest trochę brzydkie, gdy mamy generatory. Czy oni pracują?
mpen
2
@ Mark Nie wiem, czy generatory mają tu naprawdę zastosowanie. Kluczową sprawą w kontekście JSX jest wyrażenie, które zwraca tablicę. Więc jeśli zamierzasz użyć generatora, prawdopodobnie i tak byś go rozpowszechnił, i prawdopodobnie byłby bardziej gadatliwy niż rozwiązania oparte na ES2015 tutaj.
JMM
2
Jak jest to bardziej gadatliwy? I dlaczego JSX nie akceptuje iterowalnych, nie tylko tablic?
mpen
2
@ Mark To jest rzeczywiście mniej gadatliwy niż przykład z (ES5) IIFE, którego użyłem, ale jest znacznie bardziej gadatliwy niż [...Array(x)].map(() => <El />))wersja, która moim zdaniem jest najbardziej elegancka i dlaczego ją przedstawiłem. Re: drugie pytanie, to świetna uwaga. Wydaje się, że nie ma w JSX nic, co by go wykluczało, więc zależy od tego, co React lub inny konsument przekształconego JSX robi z argumentami potomnymi, czego nie mogłem powiedzieć bez patrzenia na źródło. Czy wiesz? To intrygujące pytanie.
JMM
3
@corysimmons Cool, dzięki za informacje na temat fill(). Pamiętam teraz, że powodem, dla którego się zawahałem, jest pytanie o to, jak opcjonalność parametrów jest wskazana w specyfikacji ES (trzeba to kiedyś sprawdzić). Przecinek to tylko sposób na elemen- towanie elementu tablicy - w tym przypadku pierwszego. Możesz to zrobić, jeśli chcesz, aby indeksy były od 1..długości rozkładanej tablicy, a nie od 0..długości-1 rozkładanej tablicy (ponieważ eluowane elementy tylko przyczyniają się do długości tablicy i nie dają mają odpowiednią właściwość).
JMM
85

Wystarczy użyć metody map Array ze składnią ES6:

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

Nie zapomnij o keynieruchomości.

danielfeelfine
źródło
Dodaję losowy klucz „Math.random ()” zamiast id, nie działa dobrze, gdy setState wewnątrz dzieci, masz pojęcie, dlaczego?
Oliver D
82

Korzystanie z funkcji mapowania tablic jest bardzo powszechnym sposobem zapętlania tablicy elementów i tworzenia komponentów zgodnie z nimi w React , jest to świetny sposób na wykonanie pętli, która jest dość wydajnym i uporządkowanym sposobem wykonywania pętli w JSX , to nie jedyne sposób, aby to zrobić, ale preferowany sposób.

Nie zapomnij także o posiadaniu unikalnego klucza dla każdej iteracji zgodnie z wymaganiami. Funkcja mapy tworzy unikalny indeks od 0, ale nie jest zalecane używanie wygenerowanego indeksu, ale jeśli twoja wartość jest unikalna lub jeśli istnieje unikalny klucz, możesz ich użyć:

<tbody>
  {numrows.map(x=> <ObjectRow key={x.id} />)}
</tbody>

Ponadto kilka wierszy z MDN, jeśli nie znasz funkcji mapy na tablicy:

map wywołuje podaną funkcję wywołania zwrotnego jeden raz dla każdego elementu w tablicy, i konstruuje nową tablicę na podstawie wyników. wywołanie zwrotne jest wywoływane tylko dla indeksów tablicy, które mają przypisane wartości, w tym niezdefiniowane. Nie jest wywoływany z powodu brakujących elementów tablicy (tj. Indeksów, które nigdy nie zostały ustawione, które zostały usunięte lub którym nigdy nie przypisano wartości).

wywołanie zwrotne jest wywoływane z trzema argumentami: wartością elementu, indeksem elementu i przemierzanym obiektem Array.

Jeśli parametr thisArg zostanie podany do mapowania, zostanie użyty jako wartość zwrotna tej wartości. W przeciwnym razie wartość niezdefiniowana zostanie wykorzystana jako jej wartość. Ta wartość ostatecznie obserwowalna przez wywołanie zwrotne jest ustalana zgodnie ze zwykłymi zasadami określania tego widzianego przez funkcję.

mapa nie mutuje tablicy, na której jest wywoływana (chociaż wywołanie zwrotne, jeśli zostanie wywołane, może to zrobić).

Alireza
źródło
Array#mapnie jest asynchroniczne!
philraj
52

Jeśli już używasz lodash, _.timesfunkcja jest przydatna.

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}>
                            <Select onSelect={this.onSelect}>
                                <option value="1">bacon</option>
                                <option value="2">cheez</option>
                            </Select>
                        </li>
                    )}
                </ol>
            </div>
        );
    }
}
mpen
źródło
1
Array.prototype.map może być świetną opcją tutaj, jeśli nie dołączasz biblioteki takiej jak lodash. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… .
Isaiah Gray
1
@IsaiahGrey Tylko jeśli masz już tablicę. Czasami chcesz po prostu powtórzyć kilka razy.
mpen
1
jasne oczywiście! Wiele różnych podejść na pewno w zależności od rzeczywistych danych. Przekonałem się, że częściej w naszym procesie rozwoju byliśmy w stanie dopasować mapę ze względu na sposób modelowania danych. Świetny przykład przy użyciu lodash! Przy okazji, czy używasz składni inicjalizującej właściwość, aby powiązać swoje metody ze składnikami?
Isaiah Gray
4
@IsaiahGrey Myślę, że nazywają się one definicjami metod
mpen
41

Możesz także wyodrębnić poza blok powrotu:

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

    return (<tbody>{rows}</tbody>);
}
Brandon Sibeitokgong
źródło
34

Wiem, że to stary wątek, ale możesz chcieć pobrać szablony React , które pozwalają używać szablonów w stylu jsx w reakcji, z kilkoma dyrektywami (takimi jak rt-repeat).

Twój przykład, jeśli użyjesz szablonów reagowania, to:

<tbody>
     <ObjectRow rt-repeat="obj in objects"/>
</tbody>
Etai
źródło
Dlaczego korzysta z dodatkowej biblioteki, skoro zwykły javascript już dostarczył rozwiązanie z różnymi rodzajami metody for loop lub Array.prototype.map, aby rozwiązać problem? Sam w swoim obecnym projekcie wykorzystałem już obie proste metody javascript, które działają dobrze. Mając nawyk używania prostych sposobów javascript, gdy tylko jest to możliwe, pomagam doskonalić swoje umiejętności w javascript. Korzystam z dodatkowych bibliotek tylko wtedy, gdy wydaje się, że trudno jest znaleźć rozwiązania niektórych problemów, takich jak routing z React-Router.
Lex Soft
27

Można to zrobić na wiele sposobów. JSX ostatecznie zostanie skompilowany do JavaScript, więc dopóki piszesz prawidłowy JavaScript, będziesz dobry.

Moja odpowiedź ma na celu utrwalenie wszystkich cudownych sposobów, które zostały już tutaj przedstawione:

Jeśli nie masz tablicy obiektów, po prostu liczba wierszy:

w returnbloku, tworzenie Arrayi używanie Array.prototype.map:

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

poza returnblokiem, po prostu użyj normalnej pętli for JavaScript:

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

natychmiast wywołane wyrażenie funkcji:

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

Jeśli masz tablicę obiektów

w obrębie returnbloku .map()każdy obiekt do <ObjectRow>komponentu:

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

poza returnblokiem, po prostu użyj normalnej pętli for JavaScript:

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

natychmiast wywołane wyrażenie funkcji:

render() {
  return (
    <tbody>
      {(() => {
        const rows = [];
        for (let i = 0; i < objectRows.length; i++) {
          rows.push(<ObjectRow key={i} data={objectRows[i]} />);
        }
        return rows;
      })()}
    </tbody>
  );
}
Yangshun Tay
źródło
2
Świetne odpowiedzi: różne techniki, wszystkie używają tylko zwykłego javascript, który pokazuje moc javascript, dzięki różnym sposobom wykonywania podobnych zadań.
Lex Soft
24

jeśli numrows jest tablicą i jest to bardzo proste.

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

Typ danych tablic w React jest bardzo lepszy, tablica może obsługiwać nową tablicę i wspierać filtrowanie, redukcję itp.

lulin
źródło
To nie jest przyzwoita odpowiedź, ponieważ nie korzysta z klucza.
kojow7
21

Istnieje kilka odpowiedzi wskazujących na użycie mapinstrukcji. Oto kompletny przykład, w którym iterator w składniku FeatureList wyświetla listę składników Feature na podstawie struktury danych JSON o nazwie 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>
); 

Możesz wyświetlić pełny kod FeatureList na GitHub. Lista funkcji znajduje się tutaj .

Manav Sehgal
źródło
W przypadku danych JSON, takich jak pobieranie danych z bazy danych za pomocą interfejsu API pobierania, polegam na metodzie Array.prototype.map. Jest to wygodny i sprawdzony sposób do tego celu.
Lex Soft
17

Aby zapętlić się kilka razy i wrócić, możesz to zrobić za pomocą fromi map:

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

gdzie i = number of times


Jeśli chcesz przypisać unikalne identyfikatory kluczy do renderowanych komponentów, możesz użyć React.Children.toArrayzgodnie z propozycją w dokumentacji React

React.Children.toArray

Zwraca nieprzezroczystą strukturę danych potomną jako płaską tablicę z kluczami przypisanymi do każdego dziecka. Przydatne, jeśli chcesz manipulować kolekcjami dzieci w swoich metodach renderowania, szczególnie jeśli chcesz zmienić kolejność lub pokroić this.props.children przed przekazaniem go.

Uwaga:

React.Children.toArray()zmienia klucze, aby zachować semantykę zagnieżdżonych tablic podczas spłaszczania list dzieci. Oznacza to, że toArray poprzedza każdy klucz w zwróconej tablicy, dzięki czemu klucz każdego elementu jest kierowany do tablicy wejściowej zawierającej go.

<tbody>
  {
    React.Children.toArray(
      Array.from(Array(i)).map(() => <ObjectRow />)
    )
  }
</tbody>
Jee Mok
źródło
17

Jeśli zdecyduje się na konwersję to wewnątrz return () z renderowanie metodę, najprostszym rozwiązaniem byłoby przy użyciu map () metody. Odwzoruj tablicę na składnię JSX za pomocą funkcji map (), jak pokazano poniżej ( używana jest składnia ES6 ).


Wewnątrz komponentu nadrzędnego :

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

Uwaga keyatrybut dodany do komponentu podrzędnego. Jeśli nie podałeś kluczowego atrybutu, możesz zobaczyć następujące ostrzeżenie na konsoli.

Ostrzeżenie: każde dziecko w tablicy lub iteratorze powinno mieć unikalny „klucz” prop.

Uwaga: Jednym z powszechnych błędów popełnianych przez ludzi jest używanie indexklucza podczas iteracji, używanie indexelementu jako klucza jest anty-wzorcem i możesz przeczytać więcej na ten temat tutaj . Krótko mówiąc, jeśli NIE jest to statyczna lista, nigdy nie należy jej używać indexjako klucza.


Teraz w składniku ObjectRow możesz uzyskać dostęp do obiektu z jego właściwości.

Wewnątrz komponentu ObjectRow

const { object } = this.props

lub

const object = this.props.object

Powinno to doprowadzić do obiektu przekazanego z komponentu nadrzędnego do zmiennej objectw komponencie ObjectRow . Teraz możesz wypluć wartości w tym obiekcie zgodnie ze swoim celem.


Bibliografia :

Metoda map () w JavaScript

Skrypt ECMA 6 lub ES6

bharadhwaj
źródło
16

powiedzmy, że mamy szereg przedmiotów w twoim stanie:

[{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>
Jan Franz Palngipang
źródło
Myślę, że możesz potrzebować pozbyć się {} po mapie (elemencie), co wydawało mi się, że działa na mnie.
Ian
15

Możliwość ES2015 / Babel używa funkcji generatora do utworzenia tablicy JSX:

function* jsxLoop(times, callback)
{
    for(var i = 0; i < times; ++i)
        yield callback(i);
}

...

<tbody>
    {[...jsxLoop(numrows, i =>
        <ObjectRow key={i}/>
    )]}
</tbody>
David Q Hogan
źródło
15

... Lub możesz również przygotować tablicę obiektów i odwzorować ją na funkcję, aby uzyskać pożądany wynik. Wolę to, ponieważ pomaga mi to zachować dobrą praktykę kodowania bez logiki w zwrocie renderowania.

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>
  )
}
Rafi Ud Daula Refat
źródło
15

Wystarczy użyć, .map()aby przejrzeć kolekcję i zwrócić<ObjectRow> przedmioty z rekwizytami z każdej iteracji.

Zakładając, objectsże gdzieś jest tablica ...

<tbody>
  { objects.map((obj, index) => <ObjectRow obj={ obj } key={ index }/> ) }
</tbody>
Joshua Michael Wagoner
źródło
15

ES2015 Tablica. Od Z funkcją mapy + klawisz

Jeśli nie masz nic .map(), możesz użyć Array.from()tej mapfunkcji do powtarzania elementów:

<tbody>
  {Array.from({ length: 5 }, (value, key) => <ObjectRow key={key} />)}
</tbody>
Wykonaj asynchronię
źródło
15

Używam tego:

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

PS: nigdy nie zapomnij klucza, bo będziesz miał wiele ostrzeżeń!

JC
źródło
Lub skorzystaj z indeksu tablicowego, jeśli twoje przedmioty nie mają .Idwłaściwości, np.items.map( (item, index) => <Foo key={index}>{item}</Foo>
kontur
13

Preferuję podejście, w którym logika programowania dzieje się poza wartością zwracaną render . Pomaga to utrzymać to, co w rzeczywistości jest łatwe do odczytania.

Więc prawdopodobnie zrobiłbym coś takiego:

import _ from 'lodash';

...

const TableBody = ({ objects }) => {
  const objectRows = objects.map(obj => <ObjectRow object={obj} />);      

  return <tbody>{objectRows}</tbody>;
} 

Trzeba przyznać, że jest to tak niewielka ilość kodu, że wprowadzenie go może działać dobrze.

Adam Donahue
źródło
Wielki miłośnik mapy lodash do iteracji po obiektach. Byłbym skłonny do import { map } from 'lodash'tego, abyś nie importował wszystkich funkcji lodash, jeśli ich nie potrzebujesz.
Stretch0
W dzisiejszych czasach nie potrzebujemy nawet mapy lodash - wystarczy mapować bezpośrednio tablicę.
Adam Donahue,
Tak, ale nie można zapętlać obiektu za pomocą es6 map(), tylko tablice. Właśnie to mówiłem, że lodash jest dobry do zapętlania obiektu.
Stretch0,
Myślałem, że masz na myśli, objectsjak na liście rzeczy, mój błąd.
Adam Donahue
12

możesz oczywiście rozwiązać .map, jak sugeruje druga odpowiedź. Jeśli już korzystasz z Babel, możesz pomyśleć o użyciu instrukcji jsx-control. Wymagają one trochę ustawień, ale myślę, że warto je czytać (szczególnie dla niereagujących programistów). Jeśli korzystasz z lintera, istnieje również instrukcja eslint-plugin-jsx-control

Jurgo Boemo
źródło
12

Twój kod JSX skompiluje się w czysty kod JavaScript, wszelkie tagi zostaną zastąpione ReactElementobiektami. W JavaScript nie można wywoływać funkcji wiele razy, aby zebrać zwrócone zmienne.

Jest to nielegalne, jedynym sposobem jest użycie tablicy do przechowywania zmiennych zwracanych przez funkcję.

Lub możesz użyć, Array.prototype.mapktóry jest dostępny od JavaScript ES5, aby poradzić sobie z tą sytuacją.

Może możemy napisać inny kompilator, aby odtworzyć nową składnię JSX w celu zaimplementowania funkcji powtarzania, takiej jak Angularng-repeat .

Hlissnake
źródło
12

Można to zrobić na wiele sposobów.

  1. Jak zasugerowano powyżej, przed returnzapisaniem wszystkich elementów w tablicy
  2. Pętla w środku return

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

    Metoda 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>
    )
abhirathore2006
źródło
Tak. W moim bieżącym projekcie używam metody 1, tj. Używając metody Array.prototype, forEach () do utworzenia elementu zaznaczania HTML, który jest zapełniany danymi z bazy danych. Jednak najprawdopodobniej zastąpię je metodą Array.prototype.map (), ponieważ metoda map wygląda na bardziej zwartą (mniej kodu).
Lex Soft
10

Oto proste rozwiązanie tego problemu.

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

Nie jest wymagane mapowanie i złożony kod. Wystarczy wypchnąć wiersze do tablicy i zwrócić wartości, aby je wyrenderować.

JavaScript
źródło
Podczas tworzenia elementu zaznaczania html ta podobna technika pojawiła się po raz pierwszy, więc użyłem jej, chociaż używam metody forEach (). Ale kiedy ponownie przeczytałem dokument React na temat list i kluczy, odkryłem, że używają metody map (), jak pokazano również w kilku odpowiedziach tutaj. To sprawia, że ​​myślę, że jest to preferowany sposób. Zgadzam się, ponieważ wygląda bardziej kompaktowo (mniej kodu).
Lex Soft
9

Ponieważ piszesz składnię JavaScript w kodzie JSX, musisz owinąć JavaScript w nawiasy klamrowe.

row = () => {
   var rows = [];
   for (let i = 0; i<numrows; i++) {
       rows.push(<ObjectRow/>);
   }
   return rows;
}
<tbody>
{this.row()}  
</tbody>
Rohit Jindal
źródło
9

Oto przykład z dokumentu React: JavaScript Expressions as Children

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>
  );
}

w twoim przypadku proponuję napisać w ten sposób:

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

Uwaga: Klucz jest bardzo ważny, ponieważ React używa klucza do różnicowania danych w tablicy.

Sinka Lee
źródło
9

Używam tego jak

<tbody>
  { numrows ? (
     numrows.map(obj => { return <ObjectRow /> }) 
    ) : null
  }
</tbody>
Sampath
źródło
8

Możesz zrobić coś takiego:

let foo = [1,undefined,3]
{ foo.map(e => !!e ? <Object /> : null )}
Gize Carolina Bonilla Madrazo
źródło
8

Świetne pytanie.

To, co robię, gdy chcę dodać określoną liczbę komponentów, to użycie funkcji pomocnika.

Zdefiniuj funkcję zwracającą JSX:

const myExample = () => {
    let myArray = []
    for(let i = 0; i<5;i++) {
        myArray.push(<MyComponent/>)
    }
    return myArray
}

//... in JSX

<tbody>
    {myExample()}
</tbody>
Christophe Pouliot
źródło
8

Możesz także użyć funkcji samowywołania:

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

           })()}
        </tbody>
Fba Shad
źródło
Używanie anonimowych funkcji wewnątrz renderowania nie jest uważane za dobrą praktykę w reagowaniu, ponieważ funkcje te należy odtworzyć lub odrzucić przy każdym ponownym renderowaniu.
Vlatko Vlahek
@VlatkoVlahek mylisz się z tym, że rekwizyty funkcyjne są odtwarzane w każdym cyklu renderowania, co może prowadzić do gorszej wydajności na dużą skalę. Tworzenie anonimowej funkcji w innym miejscu nie wpłynie znacząco na wydajność.
Emile Bergeron,
1
@EmileBergeron To było dawno temu haha. Zgadzam się, jeśli to nie zostanie użyte jako rekwizyt, nie ma różnicy.
Vlatko Vlahek