如何使用映射和连接渲染反应组件?

我有一个组件,它将显示字符串数组:

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]

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

95929 次浏览

返回的 <span>{t}</span>是一个对象,而不是一个字符串

通过对从 map返回的数组使用 .join(),可以加入对象的数组

您可以将逗号放在 <span></span>中,以便按照您希望的方式呈现它。

  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

可以使用 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构造一个包含所有以前元素的新数组,添加分隔符,然后添加下一个元素。

Prototype.reduce ()

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

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

一个简单的解决方案是使用 reduce(),不需要第二个参数,也不需要传播以前的结果:

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

如果没有第二个参数,reduce()将使用 从索引1开始而不是0,而且 React 对嵌套数组非常满意。

正如在注释中所说的那样,您希望只对至少有一个项的数组使用此参数,因为没有第二个参数的 reduce()将抛出一个空数组。通常情况下,这应该不是问题,因为您希望显示一个自定义消息,对于空数组,类似于“ this is void”这样的内容。

更新打字稿

你可以在 Typecript 中使用它(没有类型不安全的 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>
}
}

我的变体:

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

这对我很有效:

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

用 React 16更新: 现在可以直接呈现字符串了,所以您可以通过删除所有无用的 <span>标签来简化代码。

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

接受的答案实际上返回一个数组数组,因为 prev每次都是一个数组。React 是足够聪明的,可以做到这一点,但是它是否容易在将来造成问题,例如在给每个映射结果赋予密钥时破坏 React 差分算法。

新的 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 不会抱怨。

如果我只想呈现一个以逗号分隔的组件数组,我通常会发现 reduce过于冗长。在这种情况下,一个较短的解决方案是

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

{index > 0 && ', '}将在除第一个数组项之外的所有数组项前面呈现一个逗号和一个空格。

如果您想用其他东西(比如字符串 ' and ')来分隔倒数第二个项目和最后一个项目,那么可以用

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

正如 Pith 所提到的,React 16允许直接使用字符串,因此不再需要在 span 标记中包装字符串。在 马丁的回答的基础上,如果您还希望立即处理自定义消息(并避免在空数组上抛出错误) ,则可以在数组的 length 属性上使用三元 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>
}
}

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

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

使用 es6,你可以这样做:

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

然后跑:

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

难道只有我一个人认为答案中有很多不必要的扩散和嵌套吗?简洁的要点,当然,但它留下的大门打开问题的规模或反应改变他们如何处理嵌套数组/片段。

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>


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

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


}

从反应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>

只需在 map 之后添加 join () ,并将其放入标记中:

<span>{this.props.data.map((item) => item).join(', ')}</span>
{errors.username.map((e,k) => <span key={k}>{e} <br/></span>)}

最简单的方法。

使用 flatMap(到2022年6月为止,所有主流现代浏览器都支持) ,你只需要一个步骤就可以完成,比如:

const Demo = ({ data }) => <div>{data.flatMap(
(t, i) => [...(i ? [', '] : []), <span key={i}>{t}</span>]
)}</div>


ReactDOM.render(<Demo data={['tom', 'jason', 'chris']} />, document.body)
body { font-family: sans-serif; }
span { color: firebrick; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

如果 index = = 0,则回调返回 [span]; 否则返回 [text, span]。因为我们使用的是 flatMap而不是 map,所以产生的 [[span], [text, span], [text, span]]的2d 数组被平坦化为 [span, text, span, text, span],React 可以渲染它。