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?

Jonathan Miles
fonte
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.

Dhiraj
fonte
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;
Dmytro Medvid
fonte
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>
    );
}
Mathdoy
fonte
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>
);
conradj
fonte
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>
Nasar uddin
fonte
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