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

Posted

技术标签:

【中文标题】在 React 中迭代数据数组时呈现 JSX 元素的最有效方法【英文标题】:Most efficient way of rendering JSX elements when iterating on array of data in React 【发布时间】:2019-02-10 02:24:00 【问题描述】:

我有一个包含对象的数组。我正在创建此数组的映射以使用 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 元素的不同方式。除了这两种之外,还有其他方法可以做到这一点吗?如果有,推荐哪个?

【问题讨论】:

"Functionality2" 在调用 .map 时创建一个从未使用过的数组。切换到data.forEach,这样就不会创建不必要的数组了。 但这仅在分配给参考权时?比如 const array = data.map(如果我错了请纠正我 不,无论您是否将其分配给变量,都会创建数组。在上面的代码中,创建了 Array,然后立即有资格进行垃圾收集,因为没有引用指向它。如果切换到.forEach,则不会创建额外的Array。 好的,明白了。谢谢。当我们在迭代中渲染 jsx 元素时,您还可以帮助我使用其他渲染方式吗?除了我提到的和下面提到的其他内容 对您的两种方法以及一些变体进行快速的JsPerf 比较。功能 1 是您的最佳选择。 【参考方案1】:

第一种方法更好。

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

    Functionality-1 读起来更好。这就是通常编写 React 代码的方式。对于像这样的简单元素,我会保存项目的 const 定义,并将 map 语句放在 JSX 中以便直接返回。

【讨论】:

【参考方案2】:

我会这样做

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 不会在每次渲染时都重新实例化,并且您不必垃圾收集任何不必要的变量声明。

【讨论】:

赞成。我要补充一点,如果您想避免将 &lt;span&gt; 元素包装在 &lt;div&gt; 中,您可以使用 React Fragments:reactjs.org/docs/fragments.html【参考方案3】:

您可以使用它来更好地理解

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

export default class App extends React.Component 
  render() 
    return (
      <div>
        data.map((data, dataIndex ) => <span key=dataIndex>data.name</span>)
      </div>
    );
  

这里可以理解为name属于with属性。

【讨论】:

map-method提供的索引不推荐作为元素键值使用。【参考方案4】:

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

创建一个渲染项目的组件

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

请参阅 this post 为什么我在使用索引作为键时遵循此语法。


更新:

如果要避免使用不必要的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 />

注意:

    仅当您没有任何其他属性时,您才能使用 &lt;&gt;&lt;/&gt; 作为 &lt;React.Fragment&gt;&lt;/React.Fragment&gt; 的别名。由于我们在其上使用 key 属性,而不是使用它。 查看this 以支持React.Fragment 的短符号。

使用&lt;&gt;&lt;/&gt;的示例:

<>d.name</>

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

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

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

例子:

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

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

更新2:

另外,如果你有long list of 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。但是如果需要像&lt;div&gt;modalData&lt;/div&gt; 这样的代码呢?那么,你也可以同意我的偏好。

【讨论】:

谢谢。在第一次看到之前,我从未尝试过这样的方式。 很高兴为您提供帮助。 索引适合用作此处的键,但通常被认为是不好的做法。答案没有提到这一点。 @estus 与某个常数值一起使用时,它不会被认为是坏的。【参考方案5】:

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

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

虽然这是一个反模式:

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

map 应该将数组元素映射到其他值(因此得名),而不是迭代数组而不是 forEach 或其他循环。这个问题可以用 ESLint array-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 元素怎么办。我还能为此使用无状态组件吗? @Think-Twice 可能是的,这取决于这些处理程序应该做什么。你能用另一个例子来更新这个问题吗?【参考方案6】:

第一种方法是正确的。使用 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>
                )             
        );
    

【讨论】:

【参考方案7】:

我会在 return(...) 中使用 map,因为 map 返回一个数组。它更干净、更易读,这是我个人努力的目标。

为了补充答案,如果数组 data 可能会改变方向,我会创建一个新的无状态转储组件:

const Spanner = props =>  return <span key= props.data.id > props.data.name </span> ;

class App extends Component 
  render() 
    let data = ["id": "01", "name": "Hi", "id": "02", "name": "Hello"];
      return (
        <div>
           
            data.map( (item, ind) => 
              return (<Spanner key=item.id data=item.name />);
            )
          
        </div>
      );
    
  

这样我们就可以把这个 Spanner 组件当作一个公共组件在不同组件之间共享。如果data 随时间变化(大多数情况下会这样),您可以将包装函数写入 Spanner 组件并在需要时调用新函数。 data=["id": "01", "name": "Hi", "userType": "Admin", "id": "02", "name": "Hello", "userType": "Client"];

const UserWidget = (props) => 
  return (
    <div>
      <h4> props.type </h4>
      <Spanner key= props.key  data= props.name />
    </div>
  );

// now both the UserWidget and the Spanner Components can be used wherever required
// say a dashboard Component wants to use the UserWidget
class Dashboard extends Component 
  render() 
    let data = ["id": "01", "name": "Hi", "userType": "Admin", "id": "02", "name": "Hello", "userType": "Client"];
      return (
        <div>
           
            data.map( (item, ind) => 
              return (<UserWidget type= item.userType  key=item.id data=item.name />);
            )
          
        </div>
      );
    
  

这样您就不会重复自己,即使现在新的 data 数组具有新的键 userType,您的应用程序组件仍然具有预期的行为。 这也是我的做法。

【讨论】:

【参考方案8】:

函数toSpan可以复用。

class App extends React.Component 
  render() 
    const data = [id: `01`, name: `Hi`, id: `02`, name: `Hello`]
    const toSpan = (id, name) => <span key=id>name</span>
    return (
      <div>data.map(toSpan)</div>
    )
  

【讨论】:

以上是关于在 React 中迭代数据数组时呈现 JSX 元素的最有效方法的主要内容,如果未能解决你的问题,请参考以下文章

不迭代时如何解决数组中元素的eslint缺少关键道具?

如何渲染 JSX 元素数组(React 组件)

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

React入门-JSX和虚拟dom

根据条件渲染 JSX 元素

前端库-React框架:「01] 创建一个简单的 JSX 元素