在 React 中迭代数据数组时呈现 JSX 元素的最有效方法

IT技术 javascript reactjs jsx array.prototype.map
2021-04-14 19:45:05

我有一个包含对象的数组。我正在创建此数组的映射以使用span组件呈现名称

let data = [{"id": "01", "name": "Hi"}, {"id": "02", "name": "Hello"}];

我一直在使用以下两种不同的功能来迭代该对象数组,并使用 map 来呈现 JSX 元素。

功能1:

import React, { Component } from 'react';
class App extends Component {

  render() {
    let data = [{"id": "01", "name": "Hi"}, {"id": "02", "name": "Hello"}];
    const items = data.map((key, i) => {
      return <span key={key.id}>{key.name}</span>;
    });
    return (
      <div>
        {items}
      </div>
    );
  }
}

export default App;

功能2:

import React, { Component } from 'react';
class App extends Component {

  render() {
    let data = [{"id": "01", "name": "Hi"}, {"id": "02", "name": "Hello"}];
    let rows = [];
    data.map((key, i) => {
      rows.push(<span key={key.id}>{key.name}</span>);
    });
    return (
      <div>
        {rows}
      </div>
    );
  }
}


export default App;

我知道上述两种使用map和渲染 JSX 元素的不同方式除了这两种之外,还有其他方法可以做同样的事情吗?如果是这样,推荐哪个?

6个回答

大多数情况下,我遵循以下规则:

创建一个呈现项目的组件

// in some file
export const RenderItems = ({data}) => {
  return data && data.map((d, i) => <span key={d.id}>{d.name}</span>) || null
}

钩住 RenderItems

import { RenderItems } from 'some-file'

class App extends Component {
  render() {
    // you may also define data here instead of getting data from props
    const { data } = this.props
    return (
      <div>
        <RenderItems data={data} />
      </div>
    )
  }
}

在组件中附加数据

const data = [{"id": "01", "name": "Hi"}, {"id": "02", "name": "Hello"}]
<App data={data} />

即使使用您的第二个代码示例,遵循此规则也不会影响性能。推送数组中的项目并呈现项目。因为,您不是直接在渲染挂钩内工作。始终注意渲染钩子不会直接在其中实现任何逻辑。


此外,我不会id仅仅为了使用密钥而创建

const data = [{"name": "Hi"}, {"name": "Hello"}]
//... and when using index as key
.map((d, i) => <span key={'item-'+i}>
// or,
.map((d, i) => <span key={'item-'+i+'-'+d.name}>

请参阅这篇文章,为什么我在使用索引作为键时遵循此语法。


更新:

如果你想避免使用不必要的 html 标签,你可以使用React.Fragment

export const RenderItems = ({data}) => {
  return data && 
    data.map(
      (d, i) => <React.Fragment key={d.id}>{d.name}</React.Fragment>
    ) || null
}
// and when rendering, you just return the component
return <RenderItems data={data} />

笔记:

  1. 仅当您没有任何其他属性时<></><React.Fragment></React.Fragment>可以将其用作别名由于我们在其上使用 key 属性,而不是使用它。
  2. 看看这个以支持React.Fragment.

使用示例<></>

<>{d.name}</>

这将d.name在没有任何标记的情况下在 html 中呈现的值。当我们专门将现有设计转换为 React 应用程序时,这被认为是最好的。或者,可能还有其他情况。比如,我们将显示一个定义列表:

<dl>
  <dt></dt>
  <dd></dd>
  <dt></dt>
  <dd></dd>
  <dt></dd>
</dl>

并且我们不想附加不必要的 html 标签,那么使用 Fragment 会让我们的生活更轻松:

例子:

<>
  <dt>{d.term}</dt>
  <dd>{d.definition}</dd>
</>

最重要的情况是tdtr(TR 组件)中渲染元素如果我们不这样做,那么我们就违反了 HTML 规则。该组件将无法正确呈现。在react中,它会给你一个错误。

更新2:

另外,如果你有很长的props列表,如下所示:

const {
  items,
  id,
  imdbID,
  title,
  poster,
  getMovieInfo,
  addToFavorites,
  isOpen,
  toggleModal,
  closeModal,
  modalData,
} = props

你可以考虑像这样的解构:

const { items, ...other } = props
// and in your component you can use like:
<div modalData={other.modalData}>

但是,我个人更喜欢使用第一个示例代码。这是因为在开发过程中,我不需要每次都回头查看其他组件或寻找控制台。在给定的示例中,modalData={}我们很容易维护modalData={other.modalData}. 但是如果需要像这样编码<div>{modalData}</div>呢?那么,你可能也同意我的偏好。

@estus 当与一些常量值一起使用时,它不被认为是坏的。
2021-05-30 19:45:05
谢谢你。在第一次看到它之前,我从未尝试过这样的操作。
2021-06-04 19:45:05
很高兴帮助你。
2021-06-05 19:45:05
索引适合用作此处的键,但通常被认为是不好的做法。答案没有提到这一点。
2021-06-20 19:45:05

我会这样做

const data = [{id: 1, name: 'a'}, {id: 2, name: 'b'}];

export default class App extends PureComponent {
  render() {
    return (
      <div>
        {data.map(({ id, name }) => <span key={id}>{name}</span>)}
      </div>
    );
  }
}

现在,您data不会在每次渲染时重新实例化,并且您不必垃圾收集任何不必要的变量声明。

赞成。我想补充一点,如果你想避免将<span>元素包装<div>你可以使用 React Fragments 中:reactjs.org/docs/fragments.html
2021-05-22 19:45:05

第一种方式更好。

  1. Array.prototype.map在幕后创建一个数组,并在对每个元素应用修改后返回它。Functionality-1创建两个数组,而Functionality-2创建三个。

  2. Functionality-1读起来更好。这就是 React 代码通常的编写方式。对于这样一个简单的元素,我会保存常量定义的项目,并把地图声明的JSX要返回直接

通常,fororwhile语句是迭代数组的最有效方法。在非关键位置处理小阵列的方式可以被认为是微优化。

map在 React 组件中使用是惯用的,因为它足够快并且可以作为表达式的一部分返回一个值。

虽然这是一个反模式:

let rows = [];
data.map((key, i) => {
  rows.push(<span key={key.id}>{key.name}</span>);
});

map应该将数组元素映射到其他值(因此得名),而不是迭代数组而不是forEach其他循环。这个问题可以用 ESLintarray-callback-return规则来跟踪

该组件是无状态的,不需要是Component类。它可以是功能组件或PureComponent类。由于data是常量,因此不需要在每次渲染时分配:

const data = [{"id": "01", "name": "Hi"}, {"id": "02", "name": "Hello"}];

const App = props => <div>
  {data.map(({ id, name }) => <span key={id}>{name}</span>)}
</div>;
谢谢你。如果我想在迭代中生成带有事件处理函数的 jsx 元素怎么办。我还可以为此使用无状态组件吗?
2021-05-24 19:45:05
@Think-Twice 可能是的,这取决于这些处理程序应该做什么。你能用另一个例子来更新这个问题吗?
2021-06-03 19:45:05

第一种方法是正确的。使用 map 函数遍历数组。

export default class App extends React.Component{
    constructor(props){
        super(props);
        this.state = {
          data: [{"id": "01", "name": "Hi"}, {"id": "02", "name": "Hello"}];
        };
 }
    render(){
        return(
            <div>           
                {this.state.data.map((data,index)=>
                      <span key={data.id}>{data.name}</span>
                )}             
        );
    }
}