Самый эффективный способ рендеринга элементов JSX при итерации массива данных в React - PullRequest
0 голосов
/ 05 сентября 2018

У меня есть массив, который содержит объекты. Я создаю карту этого массива для отображения имен с компонентом span.

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

Я использовал следующие две различные функции для итерации этого массива объектов и использовал map для визуализации элементов JSX.

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:

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. Есть ли другие способы сделать то же самое, кроме этих двух? Если да, то что рекомендуется?

Ответы [ 8 ]

0 голосов
/ 12 сентября 2018

Функцию 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>
    )
  }
}
0 голосов
/ 11 сентября 2018

первый метод правильный. используйте функцию 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>
                )}             
        );
    }
}
0 голосов
/ 12 сентября 2018

Я бы пошел с map внутри return(...), так как карта возвращает массив. Это чище и читабельно, к чему я лично стремлюсь.

Чтобы добавить к ответу, если массив 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>
      );
    }
  }
}

Таким образом, мы можем использовать этот компонент гаечного ключа как общий компонент, общий для разных компонентов. А в случае, если 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. Опять же, вот как я это сделаю.

0 голосов
/ 08 сентября 2018

Обычно оператор 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>;
0 голосов
/ 07 сентября 2018

В основном я следую этому правилу:

Создание компонента, который отображает элементы

// 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>, только если у вас нет дополнительных свойств. Поскольку мы используем ключевое свойство для него, а не используем его.
  2. Взгляните на this , чтобы сделать поддержку краткой записи React.Fragment.

Пример использования <></>:

<>{d.name}</>

Будет отображено значение d.name в html без тега. Это считается лучшим, когда мы специально трансформируем наш существующий дизайн, чтобы реагировать на приложения. Или могут быть другие случаи. Например, мы собираемся отобразить список определений:

<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>? Тогда вы также можете согласиться с моими предпочтениями.

0 голосов
/ 07 сентября 2018

Вы можете использовать это для лучшего понимания

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

Здесь вы можете понять, что имя принадлежит атрибуту.

0 голосов
/ 05 сентября 2018

Я бы сделал это

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 не создается заново при каждом рендеринге, и вам не нужно собирать ненужные объявления переменных.

0 голосов
/ 05 сентября 2018

Первый способ лучше.

  1. Array.prototype.map создает массив за сценой и возвращает его после применения модификации для каждого элемента. Функциональность-1 создает два массива, а Функциональность-2 создает три.

  2. Функциональность-1 читается лучше. Так обычно пишется код React. Для простого элемента, подобного этому, я бы сохранил определение const для элементов и поместил оператор map в JSX , который будет возвращен напрямую .

...