Loop dentro do React JSX

1279

Estou tentando fazer algo parecido com o seguinte no React JSX(onde o ObjectRow é um componente separado):

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

Percebo e entendo por que isso não é válido JSX, pois JSXmapeia as chamadas de função. No entanto, vindo do modelo de terra e sendo novo JSX, não tenho certeza de como conseguiria o que foi mencionado acima (adicionando um componente várias vezes).

Ben Roberts
fonte
38
É importante observar que no JSX você precisa das tags {} em torno da sua sintaxe JavaScript. Isso pode ajudar facebook.github.io/react/docs/… .
Isaiah Gray
30
let todos = this.props.todos.map((todo) => {return <h1>{todo.title}</h1>})
precisa saber é o seguinte
@OverCoder, por que você colocaria todo o retorno na tag {} seria => return <h1> {todo.title} </h1> Não é?
Pravin Poudel
1
@pravinpoudel na verdade essa resposta é antiga, mais como let todos = this.props.todos.map(t => <h1>{t.title}</h1>):)
OverCoder 17/03

Respostas:

1205

Pense nisso como se estivesse apenas chamando funções JavaScript. Você não pode usar um forloop no qual os argumentos para uma chamada de função iriam:

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

Veja como a função tbodyestá passando um forloop como argumento e, claro, isso é um erro de sintaxe.

Mas você pode criar uma matriz e depois passar isso como argumento:

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

Você pode usar basicamente a mesma estrutura ao trabalhar com JSX:

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

Aliás, meu exemplo de JavaScript é quase exatamente o que esse exemplo de JSX se transforma. Brinque com o Babel REPL para ter uma ideia de como o JSX funciona.

Sophie Alpert
fonte
3
É possível que o compilador tenha mudado, pois este exemplo não parece compilar no compilador jsx , mas usar map como na resposta de FakeRainBrigand abaixo parece compilar corretamente.
rav
9
Posso prometer que o último exemplo ainda é compilado corretamente no compilador JSX mais recente.
Sophie Alpert
3
Isso funciona bem. A resposta de FakeRainBrigand funciona para uma iteração simples de matriz, mas essa resposta é essencial para cenários em que você não pode usar map(por exemplo, você não tem uma coleção armazenada em uma variável).
Kelvin
52
O React precisa atualizar o dom com eficiência; nesse caso, o pai deve atribuir um keya cada filho. Você pode
usar o seguinte código
7
Este exemplo funcionará, mas faltam alguns bits importantes, como keypropriedade e também um estilo de código que não é realmente usado nas bases de código do React. Considere esta resposta como stackoverflow.com/questions/22876978/loop-inside-react-jsx/… como correta.
okonetchnikov
867

Não tenho certeza se isso funcionará para a sua situação, mas geralmente o mapa é uma boa resposta.

Se esse era seu código com o loop for:

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

Você poderia escrever assim com o mapa :

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

Sintaxe ES6:

<tbody>
    {objects.map((object, i) => <ObjectRow obj={object} key={i} />)}
</tbody>
Brigand
fonte
43
O mapa faz mais sentido se o iterado for uma matriz; Um loop for é apropriado se for um número.
Code Whisperer
26
<tbody>{objects.map((o, i) => <ObjectRow obj={o} key={i}/>}</tbody>usando o suporte ES6 do Reactify ou Babel.
Distortum 27/07/2015
3
+1. Até estou usando isso com uma lista não ordenada (ul). <ul> {objects.map((object:string,i:number)=>{ return <li>{object}</li> })} </ul>usando TypeScript
Roberto C Navarro
5
isso é ótimo, mas você não pode mapear mais de um objeto .. para que eu usariafor..in
Damon
4
Lembre-se de que a ordem das chaves é arbitrária ao usar Object.keys. Acho que armazenar a ordem das chaves separadamente funciona bem e também a ordem no objeto literal, se necessário. Isso me permite escrever código comothis.props.order.map((k)=><ObjectRow key={k} {...this.props.items[k]} />)
tgrrr 15/03
443

Se você ainda não possui uma matriz para map()gostar da resposta de @ FakeRainBrigand e deseja incorporar isso, o layout de origem corresponde à saída mais próxima da resposta de @ SophieAlpert:

Com sintaxe do ES2015 (ES6) (funções de propagação e seta)

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

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

Re: transpiling com Babel, sua página de advertências diz que Array.fromé necessário para a propagação, mas no momento ( v5.8.23) isso não parece ser o caso ao espalhar um real Array. Eu tenho um problema de documentação aberto para esclarecer isso. Mas use por seu próprio risco ou polyfill.

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>

Combinação de técnicas de outras respostas

Mantenha o layout de origem correspondente à saída, mas torne a peça incorporada mais compacta:

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

Com sintaxe e Arraymétodos do ES2015

Com Array.prototype.fillvocê, você poderia fazer isso como uma alternativa ao uso do spread, como ilustrado acima:

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

(Acho que você realmente pode omitir qualquer argumento fill(), mas não sou 100% nisso.) Obrigado a @FakeRainBrigand por corrigir meu erro em uma versão anterior da fill()solução (consulte as revisões).

key

Em todos os casos, o keyattr alivia um aviso com a construção do desenvolvimento, mas não está acessível no filho. Você pode passar um atributo extra se desejar que o índice esteja disponível no filho. Consulte Listas e chaves para discussão.

JMM
fonte
2
Que tal yield? A inserção de elementos em uma matriz intermediária é meio feia quando temos geradores. Eles trabalham?
MPEN
2
@ Mark Não sei se os geradores são realmente aplicáveis ​​aqui. A principal coisa para isso no contexto de JSX é uma expressão que retorna uma matriz. Portanto, se você usasse um gerador de alguma forma, provavelmente o espalharia de qualquer maneira, e provavelmente seria mais detalhado do que as soluções baseadas no ES2015 aqui.
JMM 12/09
2
Como isso é mais detalhado? E por que o JSX não aceitaria iteráveis, não apenas matrizes?
MPEN
2
@ Mark Isso é realmente menos detalhado que o exemplo (ES5) IIFE que eu usei, mas é consideravelmente mais detalhado que a [...Array(x)].map(() => <El />))versão, que eu acho que é a mais elegante, e por que eu a destaquei. Re: a segunda pergunta, esse é um ótimo ponto. Parece que não existe nada sobre o JSX que o impeça, portanto depende do que o React ou outro consumidor de JSX transformado faça com os argumentos filhos, o que eu não poderia dizer sem olhar a fonte. Você sabe? É uma pergunta intrigante.
JMM 12/09
3
@corysimmons Legal, obrigado pela informação em fill(). Lembro-me agora que o motivo pelo qual hesitei é uma pergunta sobre como a opcionalidade dos parâmetros é indicada na especificação ES (tem que analisar isso em algum momento). A vírgula é apenas uma maneira de eliminar um elemento da matriz - nesse caso, o primeiro. Você pode fazer isso se quiser que os índices sejam de 1..length da matriz que você está espalhando, em vez de 0..length-1 da matriz de dispersão (porque elementos elididos apenas contribuem para o comprimento da matriz e não ' tem uma propriedade correspondente).
JMM
85

Simplesmente usando o método Array do mapa com sintaxe ES6:

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

Não esqueça a keypropriedade.

danielfeelfine
fonte
Eu adiciono uma chave aleatória 'Math.random ()' em vez de id, não está funcionando bem quando setState dentro de filhos, tem alguma idéia do porquê?
Oliver D
82

Usando mapa matriz função é maneira muito comum para fazer um loop através de uma matriz de elementos e criar componentes de acordo com eles em Reagir , esta é uma ótima maneira de fazer um loop que é muito eficiente e forma arrumada para fazer seus loops em JSX , é não a única maneira de fazê-lo, mas a maneira preferida.

Além disso, não se esqueça de ter uma chave exclusiva para cada iteração, conforme necessário. A função de mapa cria um índice exclusivo de 0, mas não é recomendado o uso do índice produzido, mas se seu valor for único ou se houver uma chave exclusiva, você poderá usá-los:

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

Além disso, poucas linhas do MDN se você não estiver familiarizado com a função de mapa na matriz:

map chama uma função de retorno de chamada fornecida uma vez para cada elemento em uma matriz, em ordem e constrói uma nova matriz a partir dos resultados. o retorno de chamada é chamado apenas para índices da matriz que atribuíram valores, incluindo indefinidos. Ele não é chamado para elementos ausentes da matriz (ou seja, índices que nunca foram definidos, que foram excluídos ou aos quais nunca foi atribuído um valor).

o retorno de chamada é chamado com três argumentos: o valor do elemento, o índice do elemento e o objeto Array que está sendo percorrido.

Se um parâmetro thisArg for fornecido para mapear, ele será usado como valor de retorno de chamada. Caso contrário, o valor indefinido será usado como seu valor. Esse valor finalmente observável pelo retorno de chamada é determinado de acordo com as regras usuais para determinar isso visto por uma função.

O mapa não altera a matriz na qual é chamado (embora o retorno de chamada, se chamado, possa fazê-lo).

Alireza
fonte
Array#mapnão é assíncrono!
philraj
52

Se você já está usando o lodash, a _.timesfunção é útil.

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
fonte
1
Array.prototype.map pode ser uma ótima opção aqui se você não estiver incluindo uma biblioteca como o lodash. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… .
Isaiah Gray
1
@IsaiahGrey Somente se você já tiver uma matriz. Às vezes você só quer repetir as coisas algumas vezes.
MPEN
1
sim claro! Muitas abordagens diferentes, com certeza, dependendo dos dados reais. Descobri que, mais frequentemente do que não em nosso processo de desenvolvimento, conseguimos ajustar o mapa devido à forma como os dados são modelados. Ótimo exemplo usando lodash! Btw, você está usando a sintaxe do inicializador de propriedades para vincular seus métodos aos seus componentes?
Isaiah Gray
4
@IsaiahGrey Eu acho que eles são apenas chamados ES6 definições de métodos
MPEN
41

Você também pode extrair fora do bloco de retorno:

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

    return (<tbody>{rows}</tbody>);
}
Brandon Sibeitokgong
fonte
34

Sei que esse é um encadeamento antigo, mas você pode fazer o checkout de React Templates , que permite usar modelos no estilo jsx em react, com algumas diretivas (como rt-repeat).

Seu exemplo, se você usasse reag-templates, seria:

<tbody>
     <ObjectRow rt-repeat="obj in objects"/>
</tbody>
Etai
fonte
Por que usa biblioteca adicional enquanto javascript comum já forneceu solução com vários tipos de método for loop ou Array.prototype.map para resolver o problema? Eu mesmo já usei as duas formas simples de javascript no meu projeto atual que funcionam bem. Ter o hábito de usar formas simples de javascript, sempre que possível, me ajuda a melhorar minhas habilidades em javascript. Só uso bibliotecas adicionais quando parece difícil encontrar soluções para certos problemas, como roteamento com o React-Router.
Lex macia
27

Existem várias maneiras de fazer isso. O JSX eventualmente é compilado para JavaScript; portanto, enquanto você estiver escrevendo JavaScript válido, será bom.

Minha resposta tem como objetivo consolidar todas as formas maravilhosas já apresentadas aqui:

Se você não possui uma matriz de objeto, basta o número de linhas:

dentro do returnbloco, criando Arraye usando Array.prototype.map:

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

fora do returnbloco, basta usar um loop for JavaScript normal:

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

expressão de função invocada imediatamente:

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

Se você tiver uma matriz de objetos

dentro do returnbloco, .map()cada objeto em um <ObjectRow>componente:

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

fora do returnbloco, basta usar um loop for JavaScript normal:

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

expressão de função invocada imediatamente:

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
fonte
2
Ótimas respostas: várias técnicas, todas usam apenas javascript simples, que mostra o poder do javascript, graças às várias maneiras de executar tarefas semelhantes.
Lex macia
24

se numrows é uma matriz, e é muito simples.

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

O tipo de dados do array no React é muito melhor, o array pode fazer backup de um novo array e oferecer suporte a filtros, reduzir etc.

lulin
fonte
Esta não é uma resposta decente, pois não utiliza uma chave.
precisa saber é o seguinte
21

Existem várias respostas apontando para o uso da mapdeclaração. Aqui está um exemplo completo usando um iterador dentro do componente FeatureList para listar os componentes do recurso com base em uma estrutura de dados JSON denominada 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>
); 

Você pode visualizar o código completo do FeatureList no GitHub. O dispositivo elétrico de recursos está listado aqui .

Manav Sehgal
fonte
Ao lidar com dados JSON, como ao recuperar dados do banco de dados usando a API de busca, confio no uso do método Array.prototype.map. É uma maneira conveniente e comprovada para esse fim.
Lex macia
17

Para repetir várias vezes e retornar, você pode obtê-lo com a ajuda de frome map:

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

Onde i = number of times


Se você deseja atribuir IDs de chave exclusivos aos componentes renderizados, pode usar React.Children.toArrayconforme proposto na documentação do React

React.Children.toArray

Retorna a estrutura de dados opaca filhos como uma matriz plana com chaves atribuídas a cada filho. Útil se você deseja manipular coleções de filhos em seus métodos de renderização, especialmente se deseja reordenar ou cortar this.props.children antes de transmiti-lo.

Nota:

React.Children.toArray()altera chaves para preservar a semântica de matrizes aninhadas ao achatar listas de filhos. Ou seja, toArray prefixa cada chave na matriz retornada para que a chave de cada elemento tenha o escopo definido na matriz de entrada que a contém.

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

Se você optar por converter esse método return () interno do método render , a opção mais fácil seria usar o método map () . Mapeie sua matriz na sintaxe JSX usando a função map (), como mostrado abaixo (a sintaxe ES6 é usada ).


Componente pai interno :

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

Observe o keyatributo adicionado ao seu componente filho. Se você não forneceu um atributo-chave, poderá ver o seguinte aviso no console.

Aviso: Cada filho em uma matriz ou iterador deve ter um suporte "chave" exclusivo.

Nota: Um erro comum que as pessoas cometem é usar indexcomo chave ao iterar, usar indexo elemento como chave é um antipadrão e você pode ler mais sobre isso aqui . Em resumo, se NÃO for uma lista estática, nunca use indexcomo chave.


Agora, no componente ObjectRow , você pode acessar o objeto a partir de suas propriedades.

Componente Inside ObjectRow

const { object } = this.props

ou

const object = this.props.object

Isso deve buscar o objeto que você passou do componente pai para a variável objectno componente ObjectRow . Agora você pode cuspir os valores nesse objeto de acordo com o seu propósito.


Referências :

método map () em Javascript

Script ECMA 6 ou ES6

bharadhwaj
fonte
16

digamos que temos uma variedade de itens em seu estado:

[{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
fonte
Eu acho que você pode precisar se livrar do {} após o mapa (item), que parecia funcionar para mim.
Ian
15

Uma possibilidade do ES2015 / Babel está usando uma função de gerador para criar uma matriz de 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
fonte
15

... Ou você também pode preparar uma matriz de objetos e mapeá-la para uma função para obter a saída desejada. Eu prefiro isso, porque me ajuda a manter as boas práticas de codificação sem lógica dentro do retorno da renderização.

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
fonte
15

Basta usar .map()para percorrer sua coleção e retornar <ObjectRow>itens com acessórios de cada iteração.

Assumindo que objectshá uma matriz em algum lugar ...

<tbody>
  { objects.map((obj, index) => <ObjectRow obj={ obj } key={ index }/> ) }
</tbody>
Joshua Michael Wagoner
fonte
15

ES2015 Array.from com a função de mapa + tecla

Se você não tem nada, .map()pode usar Array.from()com a mapfunção para repetir elementos:

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

Eu uso isso:

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

PS: nunca esqueça a chave ou você terá muitos avisos!

JC
fonte
Ou usar o índice da matriz se os itens não tem uma .Idpropriedade, comoitems.map( (item, index) => <Foo key={index}>{item}</Foo>
kontur
13

Eu tendem a favorecer uma abordagem em que a lógica de programação acontece fora do valor de retorno de render . Isso ajuda a manter fácil o que realmente é renderizado.

Então eu provavelmente faria algo como:

import _ from 'lodash';

...

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

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

É certo que essa é uma quantidade tão pequena de código que, se for embutida, pode funcionar bem.

Adam Donahue
fonte
Grande fã do mapa lodash para percorrer objetos. Eu gostaria import { map } from 'lodash'que você não importasse todas as funções do lodash, se você não precisar delas.
precisa saber é o seguinte
Atualmente, nem precisamos do mapa lodash - basta mapear diretamente sobre o array.
Adam Donahue
Sim, mas você não pode percorrer um objeto com es6 map(), apenas matrizes. Era isso que eu estava dizendo que o lodash é bom para fazer um loop sobre um objeto.
Stretch0
Eu pensei que você quis dizer objectscomo em uma lista de coisas, meu erro.
Adam Donahue
12

é claro que você pode resolver com um .map, conforme sugerido pela outra resposta. Se você já usa o babel, pode pensar em usar jsx-control-statement. Eles exigem um pouco de configuração, mas acho que vale a pena em termos de legibilidade (especialmente para desenvolvedores que não reagem). Se você usa um linter, também há instruções eslint-plugin-jsx-control-instructions

Jurgo Boemo
fonte
12

Seu código JSX será compilado em código JavaScript puro. Todas as tags serão substituídas por ReactElementobjetos. Em JavaScript, você não pode chamar uma função várias vezes para coletar suas variáveis ​​retornadas.

É ilegal, a única maneira é usar uma matriz para armazenar as variáveis ​​retornadas pela função.

Ou você pode usar o Array.prototype.mapque está disponível desde o JavaScript ES5 para lidar com essa situação.

Talvez possamos escrever outro compilador para recriar uma nova sintaxe JSX para implementar uma função de repetição, como a do Angularng-repeat .

hlissnake
fonte
12

Isso pode ser feito de várias maneiras.

  1. Como sugerido acima, antes de returnarmazenar todos os elementos na matriz
  2. Loop dentro return

    Método 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>
        )

    Método 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
fonte
Sim. No meu projeto atual, eu uso o método 1, ou seja, usando o método Array.prototype, forEach () para criar o elemento de seleção HTML que é preenchido por dados do banco de dados. No entanto, provavelmente os substituirei pelo método Array.prototype.map (), pois o método map parece mais compacto (menos código).
Lex macia
10

Aqui está uma solução simples para isso.

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

Nenhum mapeamento e código complexo são necessários. Você só precisa enviar as linhas para a matriz e retornar os valores para renderizá-la.

Javasamurai
fonte
Ao criar o elemento de seleção html, essa técnica semelhante foi a que me veio à cabeça em primeiro lugar, então eu a usei, embora use o método forEach (). Porém, quando reli o documento React sobre tópicos de Listas e chaves, descobri que eles usam o método map (), como também é mostrado por várias respostas aqui. Isso me faz pensar que é o caminho preferido. Eu concordo, pois parece mais compacto (menos código).
Lex macia
9

Como você está escrevendo a sintaxe Javascript no código JSX, é necessário agrupar seu Javascript entre chaves.

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

Aqui está um exemplo do React doc: Expressões JavaScript como Filhos

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

como seu caso, sugiro escrever assim:

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

Observe que a chave é muito importante, porque o React usa a chave para diferenciar os dados na matriz.

Sinka Lee
fonte
9

Eu uso assim

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

Você pode fazer algo como:

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

Ótima pergunta.

O que faço quando quero adicionar um certo número de componentes é usar uma função auxiliar.

Defina uma função que retorne JSX:

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

//... in JSX

<tbody>
    {myExample()}
</tbody>
Christophe Pouliot
fonte
8

Você também pode usar uma função de auto-chamada:

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

           })()}
        </tbody>
Fba Shad
fonte
O uso de funções anônimas dentro da renderização não é considerado uma boa prática na reação, pois essas funções precisam ser recriadas ou descartadas em cada nova renderização.
Vlatko Vlahek
@VlatkoVlahek você está confundindo com adereços de funções sendo recriados a cada ciclo de renderização, o que pode levar a um desempenho menor em grande escala. Criar uma função anônima em qualquer outro lugar não afetará o desempenho de maneira significativa.
Emile Bergeron
1
@EmileBergeron Isso foi há um tempo atrás haha. Concordo que se isso não for usado como suporte, não há diferença.
Vlatko Vlahek