web-dev-qa-db-ja.com

React)のデータの配列を反復処理するときにJSX要素をレンダリングする最も効率的な方法

オブジェクトを含む配列があります。 spanコンポーネントを使用して名前をレンダリングするために、この配列のマップを作成しています。

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

私は、以下の2つの異なる機能を使用してオブジェクトの配列を反復処理し、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要素をレンダリングする上記の2つの異なる方法を知っていました。これら2つ以外に、同じことを行う他の方法はありますか?もしそうなら、どちらが推奨されますか?

15
Hemadri Dasari

ほとんどの場合、私はこのルールに従います。

アイテムをレンダリングするコンポーネントを作成します

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

このルールに従うことは、コードの2番目の例でもパフォーマンスに影響を与えません。配列内のアイテムをプッシュし、アイテムをレンダリングします。なぜなら、レンダーフック内で直接作業しているわけではないからです。レンダーフックがその内部にロジックを直接実装しないように常に注意してください。


さらに、キーを使用するためだけに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>のエイリアスとして使用できます。キープロパティを使用しているので、使用していません。
  2. this を見て、React.Fragmentの短い表記をサポートしてください。

<></>の使用例:

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

最も重要なケースは、td(TRコンポーネント)のtr要素をレンダリングする場合です。そうでない場合は、HTMLのルールに違反しています。コンポーネントは正しくレンダリングされません。反応すると、エラーがスローされます。

Update2:

また、 小道具の長いリスト 以下のようになっている場合:

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>のようにコーディングする必要がある場合はどうでしょうか。次に、あなたも私の好みに同意するかもしれません。

10

私はこれをします

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がすべてのレンダリングで再インスタンス化されるわけではなく、不要な変数宣言をガベージコレクションする必要がありません。

12
Daniel Lizik

最初の方法の方が優れています。

  1. Array.prototype.mapは舞台裏で配列を作成し、各要素に変更を適用した後にそれを返します。 Functionality-1は2つの配列を作成し、Functionality-2は3つの配列を作成します。

  2. Functionality-1の方が読みやすくなっています。 Reactコードは通常書かれています。このような単純な要素の場合、const定義を保存します。アイテムを作成し、mapステートメントを[〜#〜] jsx [〜#〜]に入れて、直接返されます

8
Ying Zuo

一般に、forまたはwhileステートメントは、配列を反復する最も効率的な方法です。重要でない場所で小さなアレイを処理する方法は、マイクロ最適化と見なすことができます。

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>;
5
Estus Flask

最初の方法は正しいです。 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>
                )}             
        );
    }
}
2
Pravesh Sharma

Mapが配列を返すので、return(...)内の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が含まれていても、AppComponentは期待どおりの動作を維持します。繰り返しますが、これは私がそれを行う方法です。

2
Tenzin Kunkyab

関数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>
    )
  }
}
1
sultan

あなたは最もよく理解するためにこれを使うかもしれません

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

ここで、名前が属性付きに属していることがわかります。

1
Nilanth