简体   繁体   English

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

[英]Most efficient way of rendering JSX elements when iterating on array of data in React

I have an array which contains objects. 我有一个包含对象的数组。 I am creating a map of this array to renders the names with a span component. 我正在创建此数组的映射以使用span组件呈现名称。

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

I have been using the below two different functionalities to iterate on that array of objects, and using map to render JSX elements. 我一直在使用以下两个不同的功能来迭代该对象数组,并使用map来呈现JSX元素。

Functionality1: Functionality1:

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;

Functionality2: Functionality2:

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;

I known to the above two different ways of using map and rendering JSX elements. 我知道上面两种使用map和渲染JSX元素的方法。 Is there any other ways of doing the same, apart from these two? 除了这两个之外,还有其他方法可以做同样的事吗? If so, which is recommended? 如果是这样,推荐哪个?

I would do this 我会这样做的

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

Now, your data is not reinstantiated on every render, and you don't have to garbage collect any unnecessary variable declarations. 现在,您的data不会在每次渲染时重新实例化,并且您不必垃圾收集任何不必要的变量声明。

Mostly, I follow this rule: 大多数情况下,我遵循这条规则:

Create a component which renders the items 创建一个呈现项目的组件

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

Hook the RenderItems 钩住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>
    )
  }
}

Attach the data in the component 将数据附加到组件中

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

Following this rule will not impact on performance even with your second example of code ie. 即使使用第二个代码示例,遵循此规则也不会影响性能。 pushing items in an array and rendering the items. 推送数组中的项目并呈现项目。 Because, you're not directly working inside the render hook. 因为,你不是直接在渲染钩子里工作。 Always take care that render hook wouldn't implement any logic inside it directly. 始终注意渲染钩子不会直接在其中实现任何逻辑。


Further, I wouldn't create id just for using key: 此外,我不会为使用密钥创建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}>

See this post why I follow this syntax while using index as key. 看到这篇文章为什么我在使用index作为键时遵循这种语法。


Update: 更新:

If you want to avoid unnecessary html tags being used, you can use React.Fragment 如果要避免使用不必要的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} />

Note: 注意:

  1. You can use <></> as an alias for <React.Fragment></React.Fragment> only if you don't have any additional property. 只有当您没有任何其他属性时,才能使用<></>作为<React.Fragment></React.Fragment>的别名。 Since we're using key property on it, not using it. 因为我们正在使用它的关键属性,而不是使用它。
  2. Take a look at this to make support for short notation of React.Fragment . 看看这个 ,以支持React.Fragment简短表示法。

Example using <></> : 使用<></>示例:

<>{d.name}</>

This will be rendered d.name 's value in html without any tag. 这将在没有任何标记的html中呈现d.name的值。 This is considered best when we specifically transform our existing design to react application. 当我们专门改造现有设计以应对应用时,这被认为是最好的。 Or, there might be other cases. 或者,可能还有其他情况。 Like, we are going to display a definition list: 就像,我们将显示一个定义列表:

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

And we don't want to attach unnecessary html tag, then using Fragment will make our life easier: 我们不想附加不必要的html标签,然后使用Fragment将使我们的生活更轻松:

Example: 例:

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

The most important case will be for rendering td element in tr (a TR component). 最重要的情况是在tr (TR组件)中渲染td元素。 If we don't, then we're breaking the rule of HTML. 如果我们不这样做,那么我们就违反了HTML的规则。 The component will not be rendered properly. 组件将无法正确呈现。 In react, it will throw you an error. 在反应中,它会给你一个错误。

Update2: UPDATE2:

Also, if you have long list of props like below: 此外,如果你有很长的道具列表,如下所示:

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

You may consider destructuring like: 您可以考虑解构如下:

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

But, personally I prefer using first example code. 但是,我个人更喜欢使用第一个示例代码。 It's because while developing I won't need to look back to other component or look for the console each and every time. 这是因为在开发过程中我不需要回顾其他组件或者每次都寻找控制台。 In the given example there's key like modalData={} so we easily maintain modalData={other.modalData} . 在给定的例子中,有像modalData={}这样的键,所以我们很容易维护modalData={other.modalData} But what if it is needed to code like <div>{modalData}</div> ? 但是,如果像<div>{modalData}</div>这样的代码需要怎么办? Then, you may also agree with my preference. 那么,您也可能同意我的偏好。

The first way is better. 第一种方式更好。

  1. Array.prototype.map creates an array behind the scenes and returns it after applying the modification on each element. Array.prototype.map在幕后创建一个数组,并在对每个元素应用修改后返回它。 Functionality-1 creates two arrays, while Functionality-2 creates three. Functionality-1创建两个数组,而Functionality-2创建三个数组。

  2. Functionality-1 reads better. 功能-1读得更好。 It's how React code usually being written. 这就是React代码通常被编写的方式。 For a simple element like this, I'd save the const definition for items and put the map statement in the JSX to be returned directly . 对于这样一个简单的元素,我会保存物品常量的定义 ,并把地图声明的JSX 直接返回。

Generally, for or while statement is the most efficient way to iterate an array. 通常, forwhile语句是迭代数组的最有效方法。 The way a small array is processed in non-critical place can be considered microoptimisation. 在非关键位置处理小阵列的方式可以被认为是微优化。

The use of map is idiomatic in React components because it's fast enough and can return a value as a part of an expression. 在React组件中使用map是惯用的,因为它足够快并且可以将值作为表达式的一部分返回。

While this is an antipattern: 虽然这是一个反模式:

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

map is supposed to map array elements to other values (hence the name), not to iterate an array instead of forEach or other loop. map应该将数组元素映射到其他值(因此名称),而不是迭代数组而不是forEach或其他循环。 This problem can be tracked with ESLint array-callback-return rule. 可以使用ESLint array-callback-return规则跟踪此问题。

The component is stateless and doesn't need to be Component class. 该组件是无状态的,不需要是Component类。 It can be functional component or PureComponent class. 它可以是功能组件或PureComponent类。 Since data is constant, it doesn't need to be assigned on each render: 由于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>;

the first method is correct. 第一种方法是正确的。 use the map function to iterate through the array. 使用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> )} ); } } 

I'd go with the map inside the return(...) as map returns an array. 我会与去mapreturn(...)作为地图返回数组。 It's cleaner and readable, something I personally strive for. 它更干净,更易读,是我个人追求的。

To add on to the answer, if the array data might change down the lane I'd go with creating a new stateless dump component: 要添加答案,如果数组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>
      );
    }
  }
}

This way we can use this Spanner Component as a common component shared among different component. 这样我们就可以将此Spanner组件用作不同组件之间共享的公共组件。 And in case the data changes over time ( which most of the time, does) you can write a wrapper function to the Spanner component and call the new function wherever required. 如果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>
      );
    }
  }
}

This way you're not repeating yourself, and your App Component still has the expected behavior even if now the new data array has the new key userType . 这样您就不会重复自己,即使现在新data数组具有新的关键userType ,您的应用程序组件仍然具有预期的行为。 Again this is how I'd do it. 这就是我如何做到的。

You may use this for best understanding 您可以使用它来获得最佳理解

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

Here you can understand that the name belongs to with attribute. 在这里,您可以理解该名称属于with属性。

The function toSpan can be reused. 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>
    )
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM