Самый эффективный способ рендеринга элементов JSX при итерации по массиву данных в React
У меня есть массив, содержащий объекты. Я создаю карту этого массива, чтобы отображать имена с компонентом span
.
let data = [{"id": "01", "name": "Hi"}, {"id": "02", "name": "Hello"}];
Я использовал две разные функции для итерации по этому массиву объектов и использования карты для рендеринга элементов 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. Есть ли другие способы сделать то же самое, кроме этих двух? Если да, то это рекомендуется?
Ответы
Ответ 1
В основном, я следую этому правилу:
Создайте компонент, который отображает элементы
// 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} />
Замечания:
- Вы можете использовать
<></>
как псевдоним для <React.Fragment></React.Fragment>
только если у вас нет дополнительного свойства. Поскольку мы используем ключевое свойство на нем, не используем его. - Взгляните на это, чтобы поддержать короткую нотацию
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. Компонент не будет отображаться правильно. В ответ вы получите ошибку.
Ответ 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
не восстанавливаются на каждом рендере, и вам не нужно мусор собирать ненужные объявления переменных.
Ответ 3
Первый способ лучше.
-
Array.prototype.map
создает массив за кулисами и возвращает его после применения модификации для каждого элемента. Функциональность-1 создает два массива, а Functionality-2 создает три.
-
Функциональность-1 читается лучше. Это как обычно записывается код React. Для простого элемента, подобного этому, я бы сохранил определение const для элементов и сразу же вернул оператор отображения в JSX.
Ответ 4
Как правило, оператор 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
Вы можете использовать это для лучшего понимания
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>
);
}
}
Здесь вы можете понять, что имя принадлежит атрибуту.
Ответ 6
первый метод правильный. используйте функцию карты для итерации по массиву.
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
Я бы пошел с 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>
);
}
}
}
Таким образом, мы можем использовать этот компонент 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>
)
}
}