在 React JSX 中循环

Posted

技术标签:

【中文标题】在 React JSX 中循环【英文标题】:Loop inside React JSX 【发布时间】:2016-10-16 21:11:24 【问题描述】:

我正在尝试在 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) =&gt; return &lt;h1&gt;todo.title&lt;/h1&gt;) 相关:How can I render repeating React elements? @OverCoder 为什么要将整个返回放入 标记中 => 返回

todo.title

不是吗?
@pravinpoudel 实际上这个答案很旧,更像let todos = this.props.todos.map(t =&gt; &lt;h1&gt;t.title&lt;/h1&gt;) :) 【参考方案1】:

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

【讨论】:

【参考方案2】:

我不确定这是否适合您的情况,但通常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>

【讨论】:

如果迭代对象是数组,则映射更有意义;如果是数字,则 for 循环是合适的。 &lt;tbody&gt;objects.map((o, i) =&gt; &lt;ObjectRow obj=o key=i/&gt;&lt;/tbody&gt; 使用 Reactify 的 ES6 支持或 Babel。【参考方案3】:

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

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

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

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

Re: 使用 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 方法

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

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

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

key

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

【讨论】:

请不要仅仅为了从 0..N 迭代而创建一个数组。这是 Javascript 的巅峰之作。【参考方案4】:

简单地使用 ma​​p Array 方法和 ES6 语法:

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

不要忘记key 属性。

【讨论】:

我添加了一个随机的“Math.random()”键而不是 id,当 setState 在儿童中时它不能正常工作,知道为什么吗?【参考方案5】:

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

此外,不要忘记根据需要为每次迭代设置一个唯一键ma​​p函数从0开始创建唯一索引,但不建议使用生成的索引,但如果你的值是唯一的或者有唯一的键,你可以使用它们:

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

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

map 为每个元素调用一次提供的回调函数 数组,按顺序排列,并根据结果构造一个新数组。打回来 仅对具有赋值的数组索引调用, 包括未定义。它不要求缺少的元素 数组(即从未设置过的索引, 已删除或从未被赋值)。

回调使用三个参数调用:元素的值, 元素的索引,以及被遍历的 Array 对象。

如果向地图提供了 thisArg 参数,它将被用作 回调的 this 值。否则,值 undefined 将用作 它的这个值。回调最终可观察到的这个值是 根据通常的规则确定 通过函数。

map 不会改变调用它的数组(尽管 回调,如果被调用,可能会这样做)。

【讨论】:

Array#map 不是异步的!【参考方案6】:

如果您已经在使用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>
    );
  

【讨论】:

【参考方案7】:

你也可以在返回块之外提取:

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

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

【讨论】:

【参考方案8】:

有多种方法可以做到这一点。 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() 每个对象都指向一个&lt;ObjectRow&gt; 组件:

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

【讨论】:

【参考方案9】:

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

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

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

【讨论】:

【参考方案10】:

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

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

React 中的数组数据类型要好得多。一个数组可以支持一个新的数组,并且支持filter、reduce等。

【讨论】:

【参考方案11】:

如果您选择在 render 方法的 return() 内进行转换,最简单的选择是使用 ma​​p( ) 方法.使用 map() 函数将数组映射为 JSX 语法,如下所示(使用 ES6 语法)。


父组件内部

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

请注意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

【讨论】:

【参考方案12】:

有几个答案指向使用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 code。 features fixture is listed here。

【讨论】:

在处理 JSON 数据时,例如使用 fetch API 从数据库中检索数据时,我依赖于使用 Array.prototype.map 方法。这是一种方便且经过验证的方法。【参考方案13】:

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

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

在哪里i = number of times


如果你想为渲染的组件分配唯一的键 ID,你可以使用React.Children.toArrayReact documentation 中的建议

React.Children.toArray

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

注意:

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

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

【讨论】:

【参考方案14】:

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

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

【讨论】:

我认为您可能需要删除地图(项目)之后的 ,这似乎对我有用。【参考方案15】:

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>

【讨论】:

【参考方案16】:

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

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

【讨论】:

【参考方案17】:

ES2015 Array.from带map函数+key

如果您对.map() 没有任何内容,您可以使用Array.from()map 函数来重复元素:

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

【讨论】:

【参考方案18】:

我用这个:

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

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

【讨论】:

如果您的项目没有.Id 属性,例如items.map( (item, index) =&gt; &lt;Foo key=index&gt;item&lt;/Foo&gt;,则使用数组索引【参考方案19】:

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

    如上所述,在return 之前将所有元素存储在数组中

    循环进入return

    方法一 让容器 = []; let arr = [1, 2, 3] //可以是任何数组,对象

    arr.forEach((val, index) => 
      container.push(
      <div key=index>
        val
      </div>)
      /**
      * 1. All loop generated elements require a key
      * 2. only one parent element can be placed in Array
      * e.g. container.push(
      *         <div key=index>
                  val
                </div>
                <div>
                this will throw error
                </div>
            )
      **/
    );
    return (
      <div>
        <div>any things goes here</div>
        <div>container</div>
      </div>
    )
    

    方法二

    return (
      <div>
        <div>any things goes here</div>
        <div>
          
            (() => 
              let container = [];
              let arr = [1, 2, 3] //can be anything array, object
              arr.forEach((val, index) => 
                container.push(
                  <div key=index>
                    val
                  </div>)
              );
              return container;
            )()
          
        </div>
      </div>
    )
    

【讨论】:

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

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

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

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

【讨论】:

【参考方案21】:

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

所以我可能会这样做:

import _ from 'lodash';

...

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

  return <tbody>objectRows</tbody>;
 

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

【讨论】:

【参考方案22】:

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

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

【讨论】:

【参考方案23】:

这里有一个简单的解决方案。

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

<tbody>Object_rows</tbody>;

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

【讨论】:

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

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

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

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

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

【讨论】:

【参考方案25】:

这是来自 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 来区分数组中的数据。

【讨论】:

【参考方案26】:

由于您是在 JSX 代码中编写 JavaScript 语法,因此您需要将 JavaScript 代码用大括号括起来。

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

<tbody>
this.row()
</tbody>

【讨论】:

【参考方案27】:

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

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

【讨论】:

【参考方案28】:

你也可以使用自调用函数:

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

           )()
        </tbody>

【讨论】:

在渲染中使用匿名函数在 React 中不被认为是一种好的做法,因为这些函数需要在每次重新渲染时重新创建或丢弃。 @VlatkoVlahek 您误认为每个渲染周期都会重新创建函数道具,这可能导致大规模性能下降。在其他任何地方创建匿名函数不会对性能产生任何重大影响。 @EmileBergeron 这是前一阵子哈哈。我同意如果这不用作道具,没有区别。【参考方案29】:

我喜欢用它

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

【讨论】:

【参考方案30】:

即使是这段代码也能完成同样的工作。

<tbody>
   array.map(i => 
      <ObjectRow key=i.id name=i.name />
   )
</tbody>

【讨论】:

解释一下。

以上是关于在 React JSX 中循环的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 React.js 在 JSX 中循环对象

[react] 写例子说明React如何在JSX中实现for循环

如何在 React JSX 中循环遍历数组中的某些项目 [重复]

即使 .map 循环通过数组的所有索引,也无法在我的 React 应用程序中的 array.map 中显示我的 JSX 中的图像

this指向数据双向流传递参数JSX中循环React中样式路由引入资源的其它方式create-react-app脚手架事件处理获取数据UI框架推荐pc桌面应用electronjs

javascript 在JSX中循环