如何使用 map 和 join 渲染 React 组件?

Posted

技术标签:

【中文标题】如何使用 map 和 join 渲染 React 组件?【英文标题】:How to render react components by using map and join? 【发布时间】:2016-03-06 04:20:02 【问题描述】:

我有一个组件将显示字符串数组。代码如下所示:

React.createClass(
  render() 
     <div>
        this.props.data.map(t => <span>t</span>)
     </div>
  
)

它工作得很好。 即如果props.data = ['tom', 'jason', 'chris'],则页面中呈现的结果将是tomjasonchris

然后,我想用逗号连接所有名字,所以我将代码更改为:

this.props.data.map(t => <span>t</span>).join(', ')

但是,渲染的结果是[Object], [Object], [Object]

我不知道如何将对象解释为要渲染的反应组件。有什么建议吗?

【问题讨论】:

【参考方案1】:

在 map 后添加 join() 并放入 tag 中:

<span>this.props.data.map((item) => item).join(', ')</span>

【讨论】:

span包围它不会改变输出[Object], [Object], [Object]【参考方案2】:

从 React 17 开始,您可以简单地:

<div>
  [1, 2, 3].map((e, i) => <p key=i>e</p>)
</div>

它会呈现为:

<div>
  <p>1</p>
  <p>2</p>
  <p>3</p>
</div>

【讨论】:

【参考方案3】:

使用 React 16 更新:现在可以直接渲染字符串,因此您可以通过删除所有无用的 &lt;span&gt; 标签来简化代码。

const list = ( data ) => data.reduce((prev, curr) => [ prev, ', ', curr ]);

【讨论】:

【参考方案4】:

您可以使用reduce 组合数组的多个元素:

React.createClass(
  render() 
     <div>
        this.props.data
        .map(t => <span>t</span>)
        .reduce((accu, elem) => 
            return accu === null ? [elem] : [...accu, ',', elem]
        , null)
     </div>
  
)

这会将累加器初始化为 null,因此我们可以将第一项包装在数组中。对于数组中的每个后续元素,我们使用...-operator 构造一个包含所有先前元素的新数组,添加分隔符,然后添加下一个元素。

Array.prototype.reduce()

【讨论】:

谢谢,正是我需要的。您可以通过将累加器初始化为空数组来删除空检查和三进制 @Larry 如果你去掉空检查和三元,并通过 [] 作为初始化器,你如何避免前导逗号? ['a'].reduce((a, e) =&gt; [...a, ',', e],[]) 产生 [",", "a"]。除非数组为空,否则不传递任何初始化程序,在这种情况下它会返回 TypeError。 @Guy 你是绝对正确的 - 我的错误是我用空格连接值,并且在渲染输出中缺少空范围【参考方案5】:

一个简单的解决方案是使用reduce() 而不使用第二个参数并且不传播先前的结果:

class List extends React.Component 
  render() 
     <div>
        this.props.data
          .map(t => <span>t</span>)
          .reduce((prev, curr) => [prev, ', ', curr])
     </div>
  

没有第二个参数,reduce() 将 start at index 1 而不是 0,React 对嵌套数组非常满意。

正如 cmets 中所说,您只想将其用于具有至少一项的数组,因为没有第二个参数的 reduce() 将抛出一个空数组。通常这应该不是问题,因为无论如何您都想为空数组显示一条自定义消息,例如“这是空的”。

Typescript 更新

您可以在 Typescript 中使用它(没有类型不安全的 any)和 .map() 上的 React.ReactNode 类型参数:

class List extends React.Component 
  render() 
     <div>
        this.props.data
          .map<React.ReactNode>(t => <span>t</span>)
          .reduce((prev, curr) => [prev, ', ', curr])
     </div>
  

【讨论】:

If the array is empty and no initialValue was provided, TypeError would be thrown.。所以它不适用于空数组。 还要注意,这递归地嵌套了prev 数组。例如[1, 2, 3, 4] 变为 [[[1,",",2],",",3],",",4] @Tamlyn:您认为我最初在答案中提到您的观点(“React 对嵌套数组非常满意”)是否足够清楚,或者我应该详细说明一下吗? 有人让这个解决方案与 React + TypeScript 一起工作吗? @Jstuff 我不得不使用任何一个。 .reduce((prev: JSX.Element, current: JSX.Element): any =&gt; [prev, (&lt;span&gt;&amp;nbsp;&lt;/span&gt;), current])【参考方案6】:
function YourComponent(props) 

  const criteria = [];

  if (something) 
    criteria.push(<strong> something </strong>);
  

  // join the jsx elements with `, `
  const elements = criteria.reduce((accu, elem) => 
    return accu === null ? [elem] : [...accu, ', ', elem]
  , null);

  // render in a jsx friendly way
  return elements.map((el, index) => <React.Fragment key= index > el </React.Fragment> );


【讨论】:

【参考方案7】:

只有我一个人认为这里的答案中有很多不必要的传播和嵌套吗?简洁的要点,当然,但它为规模问题或 React 改变它们处理嵌套数组/片段的方式打开了大门。

const joinJsxArray = (arr, joinWith) => 
  if (!arr || arr.length < 2)  return arr; 

  const out = [arr[0]];
  for (let i = 1; i < arr.length; i += 1) 
    out.push(joinWith, arr[i]);
  
  return out;
;

// render()
<div>
  joinJsxArray(this.props.data.map(t => <span>t</span>), ', ')
</div>

一个数组,没有嵌套。也没有性感的方法链接,但如果你发现自己经常这样做,你可以随时将它添加到数组原型中,或者将其包装在一个接受映射回调的函数中,以便一次性完成所有操作。

【讨论】:

【参考方案8】:

使用 es6 您可以执行以下操作:

const joinComponents = (accumulator, current) => [
  ...accumulator, 
  accumulator.length ? ', ' : '', 
  current
]

然后运行:

listComponents
  .map(item => <span key=item.id> item.t </span>)
  .reduce(joinComponents, [])

【讨论】:

【参考方案9】:

这应该返回一个平面数组。通过提供初始空数组并从结果中过滤不需要的第一个逗号来处理具有不可迭代的第一个对象的情况

[, 'b', 'c'].reduce((prev, curr) => [...prev, ', ', curr], []).splice(1) // => [, 'b', 'c']

【讨论】:

【参考方案10】:

As mentioned by Pith,React 16 允许您直接使用字符串,因此不再需要将字符串包装在 span 标签中。在Maarten's answer 的基础上,如果您还想立即处理自定义消息(并避免在空数组上引发错误),您可以在数组的长度属性上使用三元 if 语句来引导操作。这可能看起来像这样:

class List extends React.Component 
  const  data  = this.props;

  render() 
     <div>
        data.length
          ? data.reduce((prev, curr) => [prev, ', ', curr])
          : 'No data in the array'
        
     </div>
  

【讨论】:

【参考方案11】:

如果我只想渲染一个逗号分隔的组件数组,我通常会觉得reduce 太冗长了。在这种情况下,更短的解决方案是

arr.map((item, index) => (
  <Fragment key=item.id>
    index > 0 && ', '
    <Item ...item />
  </Fragment>
))

index &gt; 0 &amp;&amp; ', ' 将在除第一个之外的所有数组项之前呈现一个逗号后跟一个空格。

如果你想用别的东西分隔倒数第二个和最后一个,比如字符串' and ',你可以将index &gt; 0 &amp;&amp; ', '替换为

index > 0 && index !== arr.length - 1 && ', '
index === arr.length - 1 && ' and '

【讨论】:

我喜欢这个,对于像我这样的 React 新手来说,这个解决方案最容易理解 :) 您可能更常将&lt;Fragment&gt;&lt;/Fragment&gt; 视为&lt;&gt;&lt;/&gt; 的简写形式。但是在这种情况下,必须使用较长的版本,因为列表中的每个项目都需要一个唯一的key,并且您不能将key 添加到&lt;&gt;&lt;/&gt;。但是,如果item 已经是一个组件,您可能不需要&lt;Item&gt;【参考方案12】:

接受的答案实际上返回一个数组数组,因为prev 每次都会是一个数组。 React 足够聪明,可以完成这项工作,但它是否容易在未来引发问题,例如在为 map 的每个结果提供键时破坏 Reacts diffing 算法。

新的React.Fragment 功能让我们能够以一种易于理解的方式做到这一点,而不会出现根本问题。

class List extends React.Component 
  render() 
     <div>
        this.props.data
          .map((t, index) => 
            <React.Fragment key=index>
              <span> t</span> ,
            </React.Fragment>
          )
      </div>
  

使用 React.Fragment,我们可以简单地将分隔符 , 放在返回的 html 之外,React 不会抱怨。

【讨论】:

很棒的解决方案,但是您需要删除数组最后一个元素的逗号 你总是可以使用车床和索引来解决这个问题。 您可以通过此修改删除最后一个逗号: t index === this.props.data.length - 1 ? '' : ',' @JohnP 前面加起来比较方便:&lt;React.Fragment key=index&gt;index ? ',' : ''&lt;span&gt;t&lt;/span&gt;&lt;/React.Fragment&gt;.【参考方案13】:

这对我有用:

    data.map( ( item, i ) => 
                  return (
                      <span key=i>item.propery</span>
                    )
                   ).reduce( ( prev, curr ) => [ prev, ', ', curr ] )

【讨论】:

【参考方案14】:

我的变种:

this.props.data
    .map(item => <span>item</span>)
    .map((item, index) => [index > 0 && ', ', item ])

【讨论】:

【参考方案15】:

使用嵌套数组将“,”保留在外面。

  <div>
      this.props.data.map((element, index) => index == this.props.data.length - 1 ? <span key=index>element</span> : [<span key=index>element</span>, ", "])
  </div>

通过将数据保存到数组并修改最后一个元素来优化它,而不是一直检查它是否是最后一个元素。

  let processedData = this.props.data.map((element, index) => [<span key=index>element</span>, ", "])
  processedData [this.props.data.length - 1].pop()
  <div>
      processedData
  </div>

【讨论】:

【参考方案16】:

您返回的&lt;span&gt;t&lt;/span&gt; 是一个对象,而不是一个字符串。检查有关它的反应文档https://facebook.github.io/react/docs/jsx-in-depth.html#the-transform

通过在map 返回的数组上使用.join(),您将加入对象数组。 [object Object], ...

您可以将逗号放在&lt;span&gt;&lt;/span&gt; 中,以便按照您想要的方式呈现。

  render() 
    return (
      <div>
         this.props.data.map(
            (t,i) => <span>t this.props.data.length - 1 === i ? '' : ',' </span>
          )
        
      </div>
    )
  

样本: https://jsbin.com/xomopahalo/edit?html,js,output

【讨论】:

以上是关于如何使用 map 和 join 渲染 React 组件?的主要内容,如果未能解决你的问题,请参考以下文章

如何防止 map() 在 react-native 中一次渲染整个数组

React:如何防止在`map`中重新渲染子组件?

在没有 react-google-map 的情况下渲染谷歌地图

React 如何在嵌套数据对象中渲染动态图像

如何在 React 中渲染嵌套数组元素?

获取 React 渲染中 map 返回的数组大小