У меня есть массив, содержащий объекты. Я создаю карту этого массива, чтобы отображать имена с помощью компонента span.
let data = [{"id": "01", "name": "Hi"}, {"id": "02", "name": "Hello"}];
Я использовал следующие две разные функции для итерации этого массива объектов и использовал карту для рендеринга элементов JSX.
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;
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-элементов. Есть ли другие способы сделать то же самое, кроме этих двух? Если да, то что рекомендуется?
но это только при присвоении ссылки правильно? как const array = data.map (поправьте меня, если я ошибаюсь
Нет, массив создается независимо от того, присвоили ли вы его переменной. В приведенном выше коде массив создается и сразу же получает право на сборку мусора, поскольку на него нет ссылок. Если вы переключитесь на .forEach, дополнительный массив не будет создан.
Хорошо понял. Спасибо. Можете ли вы помочь мне с другими способами рендеринга элементов jsx, когда мы рендерим их в итерации. Помимо того, что я упомянул, и других, упомянутых ниже
Составьте быстрое JsPerf сравнение ваших двух подходов, а также некоторых вариантов. Functionnality1 - ваш лучший выбор.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Первый способ лучше.
Array.prototype.map создает массив за кулисами и возвращает его после применения модификации к каждому элементу. Функциональность-1 создает два массива, а Функциональность-2 создает три.
Функциональность-1 читается лучше. Так обычно пишется код React. Для такого простого элемента, как этот, я бы сохранил определение const для элементов и поместил оператор карты в JSX для возврата напрямую.
Я бы сделал это
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 не восстанавливается при каждом рендеринге, и вам не нужно собирать мусор для ненужных объявлений переменных.
Проголосовали. Я бы добавил, что если вы хотите избежать упаковки элементов <span> в <div>, вы можете использовать фрагменты реакции: reactjs.org/docs/fragments.html
Вы можете использовать это для лучшего понимания
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>
);
}
}
Здесь можно понять, что имя принадлежит атрибуту.
index, предоставленный map-method, не рекомендуется использовать в качестве пары "ключ-значение".
В основном я следую этому правилу:
Создайте компонент, который отображает элементы
// 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. Компонент не будет отображаться должным образом. В ответ он выдаст вам ошибку.
Кроме того, если у вас есть длинный список реквизита, как показано ниже:
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>? Тогда вы также можете согласиться с моими предпочтениями.
Спасибо. Я никогда не пробовал так, пока не увидел это в первый раз.
Рад помочь вам.
Индекс подходит для использования в качестве ключа здесь, но в целом считается плохой практикой. В ответе об этом не упоминается.
@estus при использовании с некоторым постоянным значением не считается плохим.
Как правило, оператор 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>;
Спасибо. Что, если я хочу сгенерировать элементы jsx с функциями обработчика событий в итерации. Могу ли я использовать для этого компонент без сохранения состояния?
@ Think-Twice Возможно, да, в зависимости от того, что эти обработчики должны делать. Можете ли вы дополнить вопрос другим примером, который это демонстрирует?
первый способ правильный. используйте функцию карты для итерации по массиву.
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>
)}
);
}
}Я бы пошел с 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.
И снова вот как я это сделаю.
Функцию 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>
)
}
}
«Functionality2» создает массив при вызове
.map, который никогда не используется. Переключитесь наdata.forEach, чтобы не создавать ненужный массив.