ChatGPT解决这个技术问题 Extra ChatGPT

在 React JSX 中循环

我正在尝试在 React JSX 中执行以下操作(其中 ObjectRow 是一个单独的组件):

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

我意识到并理解为什么这不是有效的 JSX,因为 JSX 映射到函数调用。但是,来自模板领域并且是 JSX 的新手,我不确定如何实现上述目标(多次添加组件)。

需要注意的是,在 JSX 中,您需要在 JavaScript 语法周围使用 { } 标记。这可能会有所帮助facebook.github.io/react/docs/…
let todos = this.props.todos.map((todo) => {return <h1>{todo.title}</h1>})
@OverCoder 为什么要将整个 return 放入 {} 标记中 => return

{todo.title}

不是吗?
@pravinpoudel 实际上这个答案很旧,更像是 let todos = this.props.todos.map(t => <h1>{t.title}</h1>) :)

U
Utsav Kumar

把它想象成你只是在调用 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 are adding 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 的工作原理。


P
Peter Mortensen

我不确定这是否适合您的情况,但通常 map 是一个很好的答案。

如果这是带有 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>

如果 iteratee 是一个数组,则 Map 更有意义;如果它是一个数字,则 for 循环是合适的。
<tbody>{objects.map((o, i) => <ObjectRow obj={o} key={i}/>}</tbody> 使用 Reactify 的 ES6 支持或 Babel。
如果您要返回多行 JSX,请在所有行周围使用括号,即 return ( ... your lines here ... )
如果嵌套组件有删除事件,请不要使用索引作为键。
S
Sophie Alpert

如果您还没有像@FakeRainBrigand 的答案那样的 map() 数组,并且想要内联它,那么源布局对应的输出比@SophieAlpert 的答案更接近:

使用 ES2015 (ES6) 语法(展开和箭头函数)

http://plnkr.co/edit/mfqFWODVy8dKQQOkIEGV?p=preview

<tbody>
  {[...Array(10)].map((x, i) =>
    <ObjectRow key={i} />
  )}
</tbody>

回复:使用 Babel 进行编译,它的 caveats page 表示传播需要 Array.from,但目前(v5.8.23)在传播实际 Array 时似乎并非如此。我有一个 documentation issue 可以澄清这一点。但使用您自担风险或使用 polyfill。

香草 ES5

Array.apply

<tbody>
  {Array.apply(0, Array(10)).map(function (x, i) {
    return <ObjectRow key={i} />;
  })}
</tbody>

内联 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>

结合其他答案的技术

保持与输出相对应的源布局,但使内联部分更紧凑:

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

使用 ES2015 语法和数组方法

使用 Array.prototype.fill,您可以将其作为使用 spread 的替代方法,如上图所示:

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

(我认为您实际上可以省略 fill() 的任何参数,但我不是 100% 的。)感谢 @FakeRainBrigand 纠正了我在早期版本的 fill() 解决方案中的错误(请参阅修订版)。

钥匙

在所有情况下,key attr 都会减轻开发构建的警告,但在子级中无法访问。如果您希望子级中的索引可用,则可以传递额外的 attr。请参阅 Lists and Keys 进行讨论。


请不要仅仅为了从 0..N 迭代而创建一个数组。那是 Javascript 的巅峰之作。
d
danielfeelfine

简单地使用 ES6 语法的 map Array 方法:

<tbody>
  {items.map(item => <ObjectRow key={item.id} name={item.name} />)} 
</tbody>

不要忘记 key 属性。


我添加了一个随机的“Math.random()”键而不是 id,当 setState 在儿童中时它不能正常工作,知道为什么吗?
P
Peter Mortensen

在 React 中,使用 Array map 函数是循环遍历元素数组并根据它们创建组件的一种非常常见的方法。这是执行循环的好方法,它非常有效,并且是在 JSX 中执行循环的整洁方法。这不是唯一的方法,而是首选的方法。

此外,不要忘记根据需要为每次迭代都有一个唯一的密钥。 map 函数从 0 开始创建唯一索引,但不建议使用生成的索引,但如果您的值是唯一的或存在唯一键,则可以使用它们:

<tbody>
  {numrows.map(x=> <ObjectRow key={x.id} />)}
</tbody>

另外,如果您不熟悉 Array 上的 map 函数,请参考 MDN 的几行代码:

map 按顺序为数组中的每个元素调用一次提供的回调函数,并根据结果构造一个新数组。回调仅针对具有赋值的数组索引调用,包括未定义的。对于数组的缺失元素(即从未设置、已删除或从未分配值的索引),不会调用它。 callback 使用三个参数调用:元素的值、元素的索引和被遍历的 Array 对象。如果向地图提供了 thisArg 参数,它将用作回调的 this 值。否则,值 undefined 将用作其 this 值。回调最终可观察到的这个值是根据用于确定函数看到的 this 的常用规则来确定的。 map 不会改变调用它的数组(尽管回调,如果被调用,可能会这样做)。


Array#map 不是异步的!
m
mpen

如果您已经在使用 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}>repeated 3 times</li>
          ))}
        </ol>
      </div>
    );
  }
}

仅从 Lodash 导入 times,而不是整个库。在开发过程中可以避免在 Lodash 发布更新时向后不兼容,但在生产中,导入整个库需要花费几秒钟的时间。
@coder9833idls 可能需要切换到 lodash-es 才能使摇树工作,不是吗?
Y
Yangshun Tay

有多种方法可以做到这一点。 JSX 最终会被编译成 JavaScript,所以只要你编写了有效的 JavaScript,你就可以了。

我的回答旨在巩固这里已经介绍的所有精彩方式:

如果您没有对象数组,只需行数:

return 块中,创建一个 Array 并使用 Array.prototype.map

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

return 块之外,只需使用普通的 JavaScript for 循环:

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 块之外,只需使用普通的 JavaScript for 循环:

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

B
Brandon Sibeitokgong

您还可以在返回块之外提取:

render: function() {
    var rows = [];
    for (var i = 0; i < numrows; i++) {
        rows.push(<ObjectRow key={i}/>);
    } 

    return (<tbody>{rows}</tbody>);
}

P
Peter Mortensen

您可能想查看 React Templates,它允许您在 React 中使用 JSX 样式的模板,并带有一些指令(例如 rt-repeat)。

您的示例,如果您使用 react-templates,将是:

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

b
bharadhwaj

如果你选择在 render 方法的 return() 中转换它,最简单的选择是使用 map() 方法。使用 map() 函数将数组映射为 JSX 语法,如下所示(使用 ES6 语法)。

在父组件内部:

<tbody>
   { objectArray.map(object => <ObjectRow key={object.id} object={object.value} />) }
</tbody>

请注意 key 属性已添加到您的子组件中。如果您没有提供关键属性,您可以在控制台上看到以下警告。

警告:数组或迭代器中的每个孩子都应该有一个唯一的“key”道具。

注意:人们常犯的一个错误是在迭代时使用 index 作为键。使用元素的 index 作为键是一种反模式,您可以阅读有关它的更多信息 here。简而言之,如果它不是静态列表,则永远不要使用 index 作为键。

现在在 ObjectRow 组件中,您可以从其属性访问该对象。

在 ObjectRow 组件内部

const { object } = this.props

或者

const object = this.props.object

这应该会获取您从父组件传递到 ObjectRow 组件中的变量 object 的对象。现在您可以根据您的目的吐出该对象中的值。

参考:

map() method in JavaScript

ECMAScript 6 or ES6


这对我有用,但我添加了“/”来结束“”标签。
P
Peter Mortensen

如果 numrows 是一个数组,那很简单:

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

React 中的数组数据类型要好得多。一个数组可以支持一个新的数组,并支持过滤、减少等。


P
Phillip

有几个答案指向使用 map 语句。这是一个完整的示例,它使用 FeatureList 组件中的迭代器来列出基于名为 features 的 JSON 数据结构的 Feature 组件。

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

您可以在 GitHub 上查看完整的 FeatureList codefeatures fixture is listed here


在处理 JSON 数据时,例如使用 fetch API 从数据库中检索数据时,我依赖于使用 Array.prototype.map 方法。为此目的,这是一种方便且经过验证的方法。
P
Peter Mortensen

假设我们在您的状态中有一系列项目:

[{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>

我认为您可能需要删除地图(项目)之后的 {},这似乎对我有用。
只是一个想法:{this.state.items?.map((item) => ,或者我最喜欢的语法坚定:{this.state.items && this.state.items.map((item) => 并从 (item) 中删除不必要的 (),因为只有一个参数。你会因为异步渲染组件而没有导入本地状态而感到沮丧。
C
Community

要循环多次并返回,可以借助 frommap 实现:

<tbody>
  {
    Array.from(Array(i)).map(() => <ObjectRow />)
  }
</tbody>

其中i = number of times

如果您想将唯一键 ID 分配给呈现的组件,您可以使用 React documentation 中建议的 React.Children.toArray

React.Children.toArray

以平面数组的形式返回子级不透明数据结构,并为每个子级分配键。如果你想在你的渲染方法中操作孩子的集合,特别是如果你想在传递它之前重新排序或切片 this.props.children 时很有用。

注意: React.Children.toArray() 在展平子列表时更改键以保留嵌套数组的语义。也就是说, toArray 为返回数组中的每个键添加前缀,以便每个元素的键的范围都限定为包含它的输入数组。

<tbody>
  {
    React.Children.toArray(
      Array.from(Array(i)).map(() => <ObjectRow />)
    )
  }
</tbody>

P
Peter Mortensen

ECMAScript 2015 / Babel 的一种可能性是使用生成器函数来创建 JSX 数组:

function* jsxLoop(times, callback)
{
    for(var i = 0; i < times; ++i)
        yield callback(i);
}

...

<tbody>
    {[...jsxLoop(numrows, i =>
        <ObjectRow key={i}/>
    )]}
</tbody>

R
Rishab Jain

这可以通过多种方式完成。

如上所述,在返回之前将所有元素存储在数组循环中返回方法1 let container = []; let arr = [1, 2, 3] //可以是任何数组,对象 arr.forEach((val, index) => { container.push(

val
) /* * * 1. 所有循环生成的元素都需要一个键 * 2. Array 中只能放置一个父元素 * eg container.push( *
val
这会抛出错误
) **/ }); return (
anythings going here
{container}
) 方法二 return (
anythings going here
< div> { (() => { let container = []; let arr = [1, 2, 3] //可以是任何数组,对象 arr.forEach((val, index) => { container.push( < div key={index}> val
) }); 返回容器; })() }
)


是的。在我当前的项目中,我使用方法 1,即使用 Array.prototype,forEach() 方法创建由数据库中的数据填充的 HTML 选择元素。但是,我更有可能将它们替换为 Array.prototype.map() 方法,因为 map 方法看起来更紧凑(代码更少)。
P
Phillip

...或者您也可以准备一个对象数组并将其映射到一个函数以获得所需的输出。我更喜欢这个,因为它可以帮助我保持良好的编码实践,而在渲染的返回中没有逻辑。

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

C
Community

ES2015 Array.from 带有 map 函数 + 键

如果您没有要 .map() 的内容,您可以将 Array.from()map 函数一起使用来重复元素:

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

K
Kate Orlova

我用这个:

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

PS:永远不要忘记钥匙,否则你会收到很多警告!


或者,如果您的项目没有 .Id 属性(例如 items.map( (item, index) => <Foo key={index}>{item}</Foo>),则使用数组索引
J
Joshua Michael Calafell

只需使用 .map() 循环您的集合并返回 <ObjectRow> 项以及每次迭代的道具。

假设 objects 是某处的数组...

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

P
Peter Mortensen

您当然可以按照其他答案的建议使用 .map 解决。如果您已经使用 Babel,您可以考虑使用 jsx-control-statements

它们需要一些设置,但我认为它在可读性方面是值得的(尤其是对于非 React 开发人员)。如果您使用 linter,还有 eslint-plugin-jsx-control-statements


P
Peter Mortensen

这是一个简单的解决方案。

var Object_rows = [];
for (var i = 0; i < numrows; i++) {
  Object_rows.push(<ObjectRow />);
}
<tbody>{Object_rows}</tbody>;

不需要映射和复杂的代码。您只需要将行推送到数组并返回值以呈现它。


在创建 html select 元素时,我首先想到了这种类似的技术,所以我使用了它,尽管我使用了 forEach() 方法。但是当我重新阅读有关列表和键主题的 React 文档时,我发现他们使用了 map() 方法,这里的几个答案也显示了这一点。这让我认为这是首选方式。我同意,因为它看起来更紧凑(更少的代码)。
E
Emile Bergeron

您的 JSX 代码将编译为纯 JavaScript 代码,任何标签都将被 ReactElement 对象替换。在 JavaScript 中,你不能多次调用一个函数来收集它们返回的变量。

这是非法的,唯一的办法就是使用数组来存储函数返回的变量。

或者您可以使用 since JavaScript ES5 可用的 Array.prototype.map 来处理这种情况。

也许我们可以编写其他编译器来重新创建一个新的 JSX 语法来实现一个重复函数,就像 Angular's ng-repeat 一样。


A
Adam Donahue

我倾向于采用一种方法,即编程逻辑发生在 render 的返回值之外。这有助于保持实际呈现的内容易于理解。

所以我可能会做类似的事情:

import _ from 'lodash';

...

const TableBody = ({ objects }) => {
  const objectRows = objects.map(obj => <ObjectRow object={obj} />);      

  return <tbody>{objectRows}</tbody>;
} 

诚然,这是如此少量的代码,内联它可能工作得很好。


P
Peter Mortensen

你可以在 React for 循环中使用 .map() 。

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

P
Peter Mortensen

这是来自 React 文档 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>
  );
}

至于你的情况,我建议这样写:

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

请注意 key 非常重要,因为 React 使用 key 来区分数组中的数据。


是否可以映射一个数字?我在控制台中尝试过,但没有成功。
P
Peter Mortensen

由于您在 JSX 代码中编写 JavaScript 语法,因此您需要将 JavaScript 代码包装在花括号中。

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

r
ravibagul91

我像这样使用它

<tbody>
  { numrows ? (
     numrows.map(obj => { return <ObjectRow /> }) 
    ) : null
  }
</tbody>

P
Phillip

您还可以使用自调用函数:

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

           })()}
        </tbody>

在渲染中使用匿名函数不被认为是反应的好习惯,因为这些函数需要在每次重新渲染时重新创建或丢弃。
@VlatkoVlahek 您误认为每个渲染周期都重新创建了功能道具,这可能会导致大规模性能下降。在其他任何地方创建匿名函数不会以任何显着方式影响性能。
@EmileBergeron 这是不久前的哈哈。我同意如果这不用作道具,没有区别。
P
Peter Mortensen

如果您真的想要一个等效的 for 循环(您只有一个数字,而不是数组),只需使用 range from Lodash

不要重新发明轮子,也不要混淆你的代码。只需使用标准实用程序库。

import range from 'lodash/range'

range(4);
// => [0, 1, 2, 3]

range(1, 5);
// => [1, 2, 3, 4]

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅