Как зациклить и визуализировать элементы в React.js без массива объектов для сопоставления?

146

Я пытаюсь преобразовать компонент jQuery в React.js, и одна из проблем, с которыми я сталкиваюсь, это рендеринг n элементов на основе цикла for.

Я понимаю, что это невозможно, или рекомендуется, и что, когда в модели существует массив, имеет смысл использовать его полностью map. Это нормально, но что делать, когда у вас нет массива? Вместо этого у вас есть числовое значение, которое соответствует заданному количеству элементов для рендеринга, тогда что вам следует делать?

Вот мой пример, я хочу добавить к элементу произвольное количество тегов span, основываясь на его иерархическом уровне. Итак, на уровне 3, я хочу 3 тега span перед текстовым элементом.

В JavaScript:

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

Кажется, я не могу этого понять или что-то похожее на работу в компоненте JSX React.js. Вместо этого мне пришлось сделать следующее: сначала создать временный массив правильной длины, а затем зациклить массив.

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

Конечно, это не может быть лучшим или единственным способом достичь этого? Чего мне не хватает?

Джонатан Майлз
источник
3
facebook.github.io/react/docs/…
совещание
Также вы можете просто сделать это: jsfiddle.net/crl/69z2wepo/19804
caub

Ответы:

242

Обновлено: на момент реакции> 0,16

Метод рендеринга не обязательно должен возвращать один элемент. Массив также может быть возвращен.

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

ИЛИ

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

Документы здесь объясняют о детях JSX


OLD:

Вы можете использовать один цикл вместо

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

Вы также можете использовать .map и Fancy ES6

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

Кроме того, вы должны обернуть возвращаемое значение в контейнер. Я использовал div в приведенном выше примере

Как говорят здесь документы

В настоящее время при рендеринге компонента вы можете вернуть только один узел; если у вас есть, скажем, список возвращаемых элементов div, вы должны заключить компоненты в элемент div, span или любой другой компонент.

Dhiraj
источник
1
Это работает, и это намного проще, спасибо. Да, я знаю, что вы должны обернуть возвращаемое значение в контейнер, я уже делаю, что это просто отсутствует в примере.
Джонатан Майлз
2
добавить ключи внутри цикла. ключи важны в реакции.
Амир Африди
4
я искал тебя всю свою жизнь
olleh
2
@ElgsQianChen Это невозможно. Он должен быть завернут в какой-то тег. Если {indents} возвращает один элемент dom с содержимым внутри, то все в порядке
Dhiraj
2
Я не понимаю, почему метод map упоминается в этом ответе, поскольку он работает только для объектов Array, что, как ясно указывает вопрос, не соответствует действительности.
flukyspore
47

Вот более функциональный пример с некоторыми функциями 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
Это выглядит как самый «Reacty» способ сделать это. Передать значения в качестве реквизита для другого подкомпонента. Спасибо!
Майкл Джованни Пумо
Это здорово! Идеально подходит для случаев, когда ваш render () тяжелый HTML.
Матфея
Чтобы сделать его более ES6, вы можете использовать import React from "react"иexport default Articles
jonlink
1
Этот ответ даже не пытается ответить на вопрос. Был ясен вопрос, как преобразовать a for loopв массив (или объект) map'able, чтобы визуализировать n элементов в компоненте React, не имея массива элементов. Ваше решение полностью игнорирует этот факт и предполагает передачу массива статей из реквизита.
Джонатан Майлз
17

Я использую Object.keys(chars).map(...)цикл в рендере

// 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
источник
Ваш ответ сработал для меня, но только после того, как я добавил chars && ...и .bind(this)в конце моей функции. Мне любопытно, почему просто Object...(так далее и так далее) не работало. Я продолжал становиться неопределенным.
m00saca
2
Это не отвечает на вопрос, в частности, говорится, что для анализа не требуется массив объектов, а в объяснении явно говорится, что я хочу преобразовать цикл for в карту для рендеринга в компоненте React. Вы заменили массив объектом, который не помогает ответить на вопрос, или добавили какое-либо дополнительное значение.
Джонатан Майлз
16

Array.from()принимает итеративный объект для преобразования в массив и дополнительную функцию карты. Вы можете создать объект со .lengthсвойством следующим образом:

return Array.from({length: this.props.level}, (item, index) => 
  <span className="indent" key={index}></span>
);
conradj
источник
Я случайно увидел твой вопрос после прослушивания на прошлой неделе, так что это был самый быстрый способ применить то, что я узнал! syntax.fm/show/043/…
conradj
1
Как раз то, что мне нужно для рендеринга количества элементов X, спасибо!
Лиран Х,
0

Я думаю, что это самый простой способ прореагировать на JS

<ul>
    {yourarray.map((item)=><li>{item}</li>)}
</ul>
Насар Уддин
источник
2
Это не отвечает на вопрос, пожалуйста, прочитайте вопрос полностью, прежде чем пытаться ответить.
Джонатан Майлз
это помогло мне и сэкономило мое время.
Аджай Малхотра