Como fazer loop e renderizar elementos no React.js sem uma matriz de objetos para mapear?


146

Estou tentando converter um componente jQuery para React.js e uma das coisas com as quais estou tendo dificuldade é renderizar n número de elementos com base em um loop for.

Entendo que isso não é possível ou recomendado e que, onde existe uma matriz no modelo, faz todo o sentido usar map. Tudo bem, mas e quando você não tem uma matriz? Em vez disso, você tem um valor numérico que equivale a um determinado número de elementos para renderizar, então o que você deve fazer?

Aqui está o meu exemplo, quero prefixar um elemento com um número arbitrário de tags de extensão com base em seu nível hierárquico. Portanto, no nível 3, quero três tags de span antes do elemento de texto.

Em javascript:

for (var i = 0; i < level; i++) {
    $el.append('<span class="indent"></span>');
}
$el.append('Some text value');

Não consigo entender isso, ou algo semelhante ao trabalho em um componente JSX React.js. Em vez disso, tive que fazer o seguinte, primeiro construindo uma matriz temporária com o comprimento correto e depois fazendo um loop na matriz.

React.js

render: function() {
  var tmp = [];
  for (var i = 0; i < this.props.level; i++) {
    tmp.push(i);
  }
  var indents = tmp.map(function (i) {
    return (
      <span className='indent'></span>
    );
  });

  return (
    ...
    {indents}
    "Some text value"
    ...
  );
}

Certamente isso não pode ser o melhor, ou a única maneira de conseguir isso? o que estou perdendo?



Também você poderia fazer isso: jsfiddle.net/crl/69z2wepo/19804
caub

Respostas:


242

Atualizado: a partir de React> 0.16

O método Render não precisa necessariamente retornar um único elemento. Uma matriz também pode ser retornada.

var indents = [];
for (var i = 0; i < this.props.level; i++) {
  indents.push(<span className='indent' key={i}></span>);
}
return indents;

OU

return this.props.level.map((item, index) => (
    <span className="indent" key={index}>
        {index}
    </span>
));

Documentos aqui explicando sobre crianças JSX


VELHO:

Você pode usar um loop

var indents = [];
for (var i = 0; i < this.props.level; i++) {
  indents.push(<span className='indent' key={i}></span>);
}
return (
   <div>
    {indents}
    "Some text value"
   </div>
);

Você também pode usar .map e fancy es6

return (
   <div>
    {this.props.level.map((item, index) => (
       <span className='indent' key={index} />
    ))}
    "Some text value"
   </div>
);

Além disso, você deve agrupar o valor de retorno em um contêiner. Eu usei div no exemplo acima

Como os documentos dizem aqui

Atualmente, na renderização de um componente, você pode retornar apenas um nó; se você tem, digamos, uma lista de divs para retornar, você deve agrupar seus componentes em uma div, span ou qualquer outro componente.


1
Isso funciona, e é muito mais simples, obrigado. Sim, eu estou ciente de que você precisa agrupar o valor de retorno no contêiner, eu estou fazendo isso já está faltando no exemplo.
Jonathan Miles

2
adicione chaves dentro do loop. chaves são importantes para reagir.
Aamir Afridi

4
Estive olhando para você toda a minha vida
olleh

2
@ElgsQianChen Não é possível. Tem que ser embrulhado com alguma etiqueta. Se {travessões} retorna um único elementos DOM com conteúdo dentro dele, então sua ok
Dhiraj

2
Não entendo por que o método map é mencionado nesta resposta, pois funciona apenas para objetos Array, que a pergunta afirma claramente que não é o caso.
Flukyspore 31/08/19

47

Aqui está um exemplo mais funcional com alguns recursos do ES6:

'use strict';

const React = require('react');

function renderArticles(articles) {
    if (articles.length > 0) {      
        return articles.map((article, index) => (
            <Article key={index} article={article} />
        ));
    }
    else return [];
}

const Article = ({article}) => {
    return ( 
        <article key={article.id}>
            <a href={article.link}>{article.title}</a>
            <p>{article.description}</p>
        </article>
    );
};

const Articles = React.createClass({
    render() {
        const articles = renderArticles(this.props.articles);

        return (
            <section>
                { articles }
            </section>
        );
    }
});

module.exports = Articles;

1
Parece a maneira mais 'Reacty' de fazer isso. Passe valores como acessórios para outro subcomponente. Obrigado!
Michael Giovanni Pumo

Isto é ótimo! Perfeito para quando seu render () é pesado em html.
matthew

Para torná-lo mais ES6, você pode usar import React from "react"eexport default Articles
jonlink 15/03

1
Essa resposta nem tenta responder à pergunta. A questão era clara: como converter um for loopem um array (ou objeto) mapeável para renderizar n número de itens em um componente React sem ter um array de itens. Sua solução ignora completamente esse fato e assume que está sendo transmitida uma variedade de artigos de adereços.
Jonathan Miles

17

estou a usar Object.keys(chars).map(...) loop na renderização

// chars = {a:true, b:false, ..., z:false}

render() {
    return (
       <div>
        {chars && Object.keys(chars).map(function(char, idx) {
            return <span key={idx}>{char}</span>;
        }.bind(this))}
        "Some text value"
       </div>
    );
}

Sua resposta funcionou para mim, mas somente depois que adicionei chars && ...e .bind(this)finalizei minha função. Estou curioso para saber por que apenas Object...(e assim por diante) não funcionou. Eu continuava indefinido.
M00saca

2
Isso não responde à pergunta, diz especificamente sem uma matriz de objetos para analisar e a explicação diz explicitamente que desejo converter um loop for para mapear para renderização em um componente React. Você substituiu a matriz por um objeto que não ajuda a responder à pergunta ou agrega mais valor.
Jonathan Miles

16

Array.from()leva um objeto iterável para converter em uma matriz e uma função de mapa opcional. Você pode criar um objeto com uma .lengthpropriedade da seguinte maneira:

return Array.from({length: this.props.level}, (item, index) => 
  <span className="indent" key={index}></span>
);

Acabei de ver sua pergunta depois de ouvi-la na semana passada, por isso foi a maneira mais rápida de aplicar o que aprendi! syntax.fm/show/043/…
conradj

1
Exatamente o que eu precisava para renderizar um número X de elementos, obrigado!
Liran H

0

Eu acho que esta é a maneira mais fácil de fazer um loop em reagir js

<ul>
    {yourarray.map((item)=><li>{item}</li>)}
</ul>

2
Isso não responde à pergunta, leia a pergunta na íntegra antes de tentar responder.
Jonathan Miles

isso me ajudou e economizou meu tempo.
Ajay Malhotra
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.