javascript - warning - typescript react foreach




Loop dentro do React JSX (20)

Eu estou tentando fazer algo como o seguinte no React JSX (onde o ObjectRow é um componente separado):

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

Eu percebo e entendo porque este não é um JSX válido, já que o JSX mapeia para chamadas de função. No entanto, vindo do template land e sendo novato no JSX, não tenho certeza de como conseguiria o acima (adicionando um componente várias vezes).


ES2015 Array.from com a função map + key

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

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

Ótima pergunta.

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

Defina uma função que retorne o JSX:

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

//... in JSX

<tbody>
    {myExample()}
</tbody>

Aqui está uma amostra do React doc: https://facebook.github.io/react/docs/jsx-in-depth.html#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>
  );
}

como seu caso, sugiro escrever assim:

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

Por favor, note que a chave é muito importante, porque React use Key para diferenciar os dados no array.


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 empurrar as linhas para array e retornar os valores para processá-lo.


Costumo favorecer uma abordagem em que a lógica de programação acontece fora do valor de retorno da render . Isso ajuda a manter o que é realmente facilitado para grok.

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 esta é uma quantidade tão pequena de código que, em linhas gerais, pode funcionar bem.


Eu sei que este é um segmento antigo, mas você pode querer verificar http://wix.github.io/react-templates/ , que permite usar modelos de estilo jsx em reagir, com algumas diretivas (como rt- repetir).

Seu exemplo, se você usou react-templates, seria:

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

Eu uso isso:

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

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


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

Minha resposta visa consolidar todas as maravilhosas formas já apresentadas aqui:

Se você não tiver uma matriz de objetos, simplesmente o número de linhas:

dentro do bloco de return , criando um Array e usando Array.prototype.map :

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

fora do bloco de return , simplesmente use um loop normal para JavaScript:

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

invocou imediatamente a expressão de função:

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

Se você tem uma matriz de objetos

dentro do bloco de return , .map() cada objeto para um componente <ObjectRow> :

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

fora do bloco de return , simplesmente use um loop normal para JavaScript:

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

invocou imediatamente a expressão de função:

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

Isso pode ser feito de várias maneiras.

  1. Como sugerido acima, antes de return armazene todos os elementos na matriz
  2. Loop dentro do 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>
    )

Não tenho certeza se isso funcionará para sua situação, mas muitas vezes o map é uma boa resposta.

Se este foi 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 map :

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

ES6 sintaxe:

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

Pense nisso como se você estivesse apenas chamando funções JavaScript. Você não pode colocar um loop for dentro de uma chamada de função:

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

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

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 o 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 transforma em. Brinque com o Babel REPL para ter uma ideia de como o JSX funciona.


Se você ainda não tem uma matriz para map() como a resposta do @FakeRainBrigand, e quer inline isto, então o layout da fonte corresponde à saída mais próxima que a resposta do @ SophieAlpert:

Com a 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 propagação, mas no momento ( v5.8.23 ) isso não parece ser o caso quando se espalha um Array real. Eu tenho uma questão de documentação aberta para esclarecer isso. Mas use a seu próprio risco ou polyfill.

Baunilha 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 alinhada 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 os métodos de sintaxe e Array ES2015

Com o Array.prototype.fill você pode fazer isso como uma alternativa ao uso do spread, conforme ilustrado acima:

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

(Eu acho que você poderia realmente omitir qualquer argumento para fill() , mas eu não estou 100% sobre isso.) Obrigado a @FakeRainBrigand por corrigir meu erro em uma versão anterior da solução fill() (veja as revisões).

key

Em todos os casos, a key attr alivia um aviso com a compilação de desenvolvimento, mas não é acessível na criança. Você pode passar um attr extra se quiser que o índice esteja disponível no filho. Veja Listas e Chaves para discussão.


Se você optar por converter isso dentro de return () 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 (), conforme mostrado abaixo (a sintaxe ES6 é usada ).

Componente pai interno :

<tbody> { objectArray.map((object, index) => <ObjectRow key={index} object={object}>) } </tbody>

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

Aviso: Cada filho em uma matriz ou iterador deve ter uma única "chave" prop.

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

Dentro do componente ObjectRow

const { object } = this.props

ou

const object = this.props.object

Isso deve buscar o objeto passado do componente pai para o object variável no componente ObjectRow . Agora você pode cuspir os valores desse objeto de acordo com o seu propósito.

Referências :

map

ECMA Script 6 ou ES6


Seu código JSX será compilado em código JavaScript puro, todas as tags serão substituídas por objetos ReactElement . 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 da função.

Ou você pode usar o map que 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 ng-repeat do Angular .


Simplesmente use .map() para percorrer sua coleção e retornar itens <ObjectRow> com adereços de cada iteração.

Assumindo objects é uma matriz em algum lugar ...

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

Uma possibilidade 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>

Você pode fazer algo como:

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

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

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>

if numrows é um array, e é muito simples.

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

O tipo de dados de matriz no React é muito melhor, o array pode suportar o novo array e o filtro de suporte, reduzir etc.





reactjs