Я пытаюсь сделать что-то вроде следующего в React JSX
(где ObjectRow является отдельным компонентом):
<tbody>
for (var i=0; i < numrows; i++) {
<ObjectRow/>
}
</tbody>
Я понимаю и понимаю, почему это недопустимо JSX
, поскольку JSX
отображается на вызовы функций. Однако, исходя из шаблона земли и будучи новичком в JSX
, я не уверен, как бы я достиг вышеуказанного (добавление компонента несколько раз).
28 ответов
Думайте об этом, как будто вы просто вызываете функции JavaScript. Вы не можете использовать цикл for
, куда будут идти аргументы для вызова функции:
return tbody(
for (var i = 0; i < numrows; i++) {
ObjectRow()
}
)
Посмотрите, как функции tbody
передается цикл for
в качестве аргумента, и, конечно, это синтаксическая ошибка.
Но вы можете создать массив, а затем передать его в качестве аргумента:
var rows = [];
for (var i = 0; i < numrows; i++) {
rows.push(ObjectRow());
}
return tbody(rows);
Вы можете использовать в основном ту же структуру при работе с 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>;
Между прочим, мой пример JavaScript почти точно то, во что превращается этот пример JSX. Поиграйте с Babel REPL, чтобы понять, как работает JSX.
Вы также можете использовать функцию самовызвания:
return <tbody>
{(() => {
let row = []
for (var i = 0; i < numrows; i++) {
row.push(<ObjectRow key={i} />)
}
return row
})()}
</tbody>
Использование функции Карта массива - это очень распространенный способ перебирать элементы массива и создавать компоненты в соответствии с ними в React , это отличный способ сделать цикл, который является довольно эффективным и аккуратным способом сделать ваши циклы в JSX , это не единственный способ сделать это, но предпочтительный способ. сильный>
Кроме того, не забывайте иметь уникальный ключ для каждой итерации, как требуется. Функция карты создает уникальный индекс из 0, но не рекомендуется использовать созданный индекс, но если ваше значение уникально или если есть уникальный ключ, вы можете использовать их:
<tbody>
{numrows.map(x=> <ObjectRow key={x.id} />)}
</tbody>
Кроме того, несколько строк из MDN , если вы не знакомы с функцией карты в массиве:
map вызывает предоставленную функцию обратного вызова один раз для каждого элемента в массиве по порядку и создает новый массив из результатов. обратный вызов вызывается только для индексов массива, которым присвоены значения, в том числе неопределенные. Он не вызывается для отсутствующих элементов массива (то есть индексов, которые никогда не были установлены, которые были удалены или которым никогда не было присвоено значение).
обратный вызов вызывается с тремя аргументами: значением элемента, индексом элемента и объектом Array, по которому осуществляется обход.
Если для сопоставления предоставлен параметр thisArg, он будет использоваться как обратное значение this. В противном случае значение undefined будет использоваться в качестве значения this. Это значение, в конечном итоге наблюдаемое обратным вызовом, определяется в соответствии с обычными правилами определения этого, видимого функцией.
map не изменяет массив, в котором он вызывается (хотя обратный вызов, если вызван, может сделать это).
Я знаю, что это старая ветка, но вы можете проверить React Templates, которая позволяет вы используете шаблоны типа jsx в реакции с несколькими директивами (такими как rt-repeat).
Ваш пример, если бы вы использовали ответные шаблоны, был бы:
<tbody>
<ObjectRow rt-repeat="obj in objects"/>
</tbody>
Вот пример из документа React: Выражения JavaScript как дети
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>
);
}
Как ваш случай, я предлагаю написать так:
function render() {
return (
<tbody>
{numrows.map((roe, index) => <ObjectRow key={index} />)}
</tbody>
);
}
Обратите внимание, что ключ очень важен, потому что React использует ключ для различения данных в массиве.
Я использую это как
<tbody>
{ numrows ? (
numrows.map(obj => { return <ObjectRow /> })
) : null
}
</tbody>
Вы, конечно, можете решить с помощью .map, как подсказывает другой ответ. Если вы уже используете babel, вы можете подумать об использовании jsx-control-Statement Они требуют небольшой настройки, но я думаю, что это стоит с точки зрения читабельности (особенно для не реагирующего разработчика). Если вы используете линтер, есть также eslint-plugin-jsx-control-Statement
Я использую это:
gridItems = this.state.applications.map(app =>
<ApplicationItem key={app.Id} app={app } />
);
PS: никогда не забывайте ключ, иначе у вас будет много предупреждений!
Есть несколько способов сделать это. JSX в конечном итоге компилируется в JavaScript, поэтому, пока вы пишете правильный JavaScript, вы будете в порядке.
Мой ответ нацелен на объединение всех чудесных способов, уже представленных здесь:
Если у вас нет массива объектов, просто количество строк:
В блоке return
создайте Array
и используйте Array.prototype.map
:
render() {
return (
<tbody>
{Array(numrows).fill(null).map((value, index) => (
<ObjectRow key={index}>
))}
</tbody>
);
}
Вне блока return
просто используйте обычный цикл for для JavaScript:
render() {
let rows = [];
for (let i = 0; i < numrows; i++) {
rows.push(<ObjectRow key={i}/>);
}
return (
<tbody>{rows}</tbody>
);
}
Сразу вызывается функция выражения:
render() {
return (
<tbody>
{() => {
let rows = [];
for (let i = 0; i < numrows; i++) {
rows.push(<ObjectRow key={i}/>);
}
return rows;
}}
</tbody>
);
}
Если у вас есть массив объектов
В блоке return
.map()
каждый объект компонента <ObjectRow>
:
render() {
return (
<tbody>
{objectRows.map((row, index) => (
<ObjectRow key={index} data={row} />
))}
</tbody>
);
}
Вне блока return
просто используйте обычный цикл for для JavaScript:
render() {
let rows = [];
for (let i = 0; i < objectRows.length; i++) {
rows.push(<ObjectRow key={i} data={objectRows[i]} />);
}
return (
<tbody>{rows}</tbody>
);
}
Сразу вызывается функция выражения:
render() {
return (
<tbody>
{(() => {
const rows = [];
for (let i = 0; i < objectRows.length; i++) {
rows.push(<ObjectRow key={i} data={objectRows[i]} />);
}
return rows;
})()}
</tbody>
);
}
Не уверен, что это будет работать в вашей ситуации, но часто Карта - хороший ответ.
Если это был ваш код с циклом for:
<tbody>
for (var i=0; i < objects.length; i++) {
<ObjectRow obj={objects[i]} key={i}>
}
</tbody>
Вы можете написать это так с map :
<tbody>
{objects.map(function(object, i){
return <ObjectRow obj={object} key={i} />;
})}
</tbody>
Синтаксис ES6:
<tbody>
{objects.map((object, i) => <ObjectRow obj={object} key={i} />)}
</tbody>
Вот простое решение для этого.
var Object_rows=[];
for (var i=0; i < numrows; i++) {
Object_rows.push(<ObjectRow/>)
}
<tbody>
{Object_rows}
</tbody>
Нет необходимости в отображении и сложном коде. Вам просто нужно поместить строки в массив и вернуть значения для его визуализации.
Возможность ES2015 / Babel - использовать функцию генератора для создания массива JSX:
function* jsxLoop(times, callback)
{
for(var i = 0; i < times; ++i)
yield callback(i);
}
...
<tbody>
{[...jsxLoop(numrows, i =>
<ObjectRow key={i}/>
)]}
</tbody>
Просто используйте метод map Array с синтаксисом ES6:
<tbody>
{items.map(item => <ObjectRow key={item.id} name={item.name} />)}
</tbody>
Не забудьте свойство key
.
ES2015 Array.from с функцией карты + клавиша
Если у вас нет ничего для .map()
, вы можете использовать Array.from()
с функцией map
для повторения элементов:
<tbody>
{Array.from({ length: 5 }, (value, key) => <ObjectRow key={key} />)}
</tbody>
Я склоняюсь к подходу, где логика программирования происходит за пределами возвращаемого значения render
. Это помогает сохранить то, что на самом деле легко сделать.
Так что я бы, наверное, сделал что-то вроде:
import _ from 'lodash';
...
const TableBody = ({ objects }) => {
const objectRows = objects.map(obj => <ObjectRow object={obj} />);
return <tbody>{objectRows}</tbody>;
}
По общему признанию, это такой небольшой объем кода, что его вставка может работать нормально.
Просто используйте .map()
, чтобы пройтись по своей коллекции и вернуть <ObjectRow>
элементы с реквизитом из каждой итерации.
Предполагая, что objects
является массивом где-то ...
<tbody>
{ objects.map((obj, index) => <ObjectRow obj={ obj } key={ index }/> ) }
</tbody>
Вы также можете извлечь вне блока возврата:
render: function() {
var rows = [];
for (var i = 0; i < numrows; i++) {
rows.push(<ObjectRow key={i}/>);
}
return (<tbody>{rows}</tbody>);
}
Ваш JSX-код будет скомпилирован в чистый код JavaScript, любые теги будут заменены ReactElement
объектами. В JavaScript вы не можете вызывать функцию несколько раз для сбора возвращаемых переменных.
Это недопустимо, единственный способ - использовать массив для хранения возвращаемых функцией переменных.
Или вы можете использовать Array.prototype.map
который доступен начиная с JavaScript ES5 чтобы справиться с этой ситуацией.
Возможно, мы сможем написать другой компилятор для воссоздания нового синтаксиса JSX для реализации функции повтора, например, Angular's ng-repeat
.
Есть несколько ответов, указывающих на использование оператора map
. Вот полный пример использования итератора в компоненте FeatureList для перечисления компонентов Feature на основе структуры данных JSON под названием 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>
);
Вы можете просмотреть полный код FeatureList на GitHub. , Список функций здесь.
Скажем, у нас есть массив элементов в вашем состоянии:
[{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>
Чтобы выполнить цикл несколько раз и выполнить возврат, вы можете выполнить его с помощью from
и map
:
<tbody>
{
Array.from(Array(i)).map(() => <ObjectRow />)
}
</tbody>
Где i = number of times
Если вы хотите назначить уникальные идентификаторы ключей для визуализируемых компонентов, вы можете использовать React.Children.toArray
, как это предлагается в Реактивная документация
< Сильный > React.Children.toArray
Возвращает дочернюю непрозрачную структуру данных в виде плоского массива с ключами, назначенными каждому дочернему элементу. Полезно, если вы хотите манипулировать коллекциями дочерних элементов в ваших методах рендеринга, особенно если вы хотите переупорядочить или нарезать this.props.children перед его передачей.
< Сильный > Примечание :
React.Children.toArray()
изменяет ключи, чтобы сохранить семантику вложенных массивов при сведении списков дочерних элементов. То есть, toArray ставит префикс перед каждым ключом в возвращаемом массиве, так что ключ каждого элемента ограничивается входным массивом, содержащим его.
<tbody>
{
React.Children.toArray(
Array.from(Array(i)).map(() => <ObjectRow />)
)
}
</tbody>
Если вы уже используете lodash, удобная функция _.times
.
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>
);
}
}
Если вы решите конвертировать это внутри метода return () метода render , проще всего будет использовать метод map () . Сопоставьте ваш массив с синтаксисом JSX с помощью функции map (), как показано ниже (используется синтаксис ES6 ).
Внутри родительского компонента :
<tbody>
{ objectArray.map(object => <ObjectRow key={object.id} object={object.value}>) }
</tbody>
Обратите внимание, что атрибут key
добавлен к вашему дочернему компоненту. Если вы не указали ключевой атрибут, вы можете увидеть следующее предупреждение на своей консоли.
Предупреждение: каждый дочерний элемент в массиве или итераторе должен иметь уникальную «ключевую» опору.
Примечание. Одна распространенная ошибка, которую делают люди, заключается в использовании index
в качестве ключа при итерации, использование index
элемента в качестве ключа является антишаблоном, и вы можете прочитать о нем подробнее здесь. Короче говоря, если НЕ статический список, никогда не используйте index
в качестве ключа.
Теперь в компоненте ObjectRow вы можете получить доступ к объекту из его свойств.
Внутри компонента ObjectRow
const { object } = this.props
Или
const object = this.props.object
Это должно извлечь вам объект, который вы передали из родительского компонента в переменную object
в компоненте ObjectRow . Теперь вы можете выплевывать значения в этом объекте в соответствии с вашей целью.
Ссылки :
Если numrows - это массив, и это очень просто.
<tbody>
{numrows.map(item => <ObjectRow />)}
</tbody>
Тип данных массива в React намного лучше, массив может поддерживать новый массив и поддерживать фильтрацию, уменьшение и т. Д.
Вы можете сделать что-то вроде:
let foo = [1,undefined,3]
{ foo.map(e => !!e ? <Object /> : null )}
... Или вы также можете подготовить массив объектов и сопоставить его с функцией, чтобы получить желаемый результат. Я предпочитаю это, потому что это помогает мне поддерживать хорошую практику кодирования без какой-либо логики в возвращении рендера.
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>
)
}
Поскольку вы пишете синтаксис Javascript внутри JSX-кода, вам необходимо заключить Javascript в фигурные скобки.
row = () => {
var rows = [];
for (let i = 0; i<numrows; i++) {
rows.push(<ObjectRow/>);
}
return rows;
}
<tbody>
{this.row()}
</tbody>
Отличный вопрос
Когда я хочу добавить определенное количество компонентов, я использую вспомогательную функцию.
Определите функцию, которая возвращает JSX:
const myExample = () => {
let myArray = []
for(let i = 0; i<5;i++) {
myArray.push(<MyComponent/>)
}
return myArray
}
//... in JSX
<tbody>
{myExample()}
</tbody>
Похожие вопросы
Связанные вопросы
Новые вопросы
javascript
По вопросам программирования на ECMAScript (JavaScript/JS) и его различных диалектах/реализациях (кроме ActionScript). Имейте в виду, что JavaScript — это НЕ то же самое, что Java! Включите все ярлыки, относящиеся к вашему вопросу; например, [node.js], [jQuery], [JSON], [ReactJS], [angular], [ember.js], [vue.js], [typescript], [svelte] и т. д.