як візуалізувати компоненти, використовуючи карту та з'єднатись


102

У мене є один компонент, який буде відображати масив рядків. Код виглядає приблизно так.

React.createClass({
  render() {
     <div>
        this.props.data.map(t => <span>t</span>)
     </div>
  }
})

Це працює чудово. тобто якщо props.data = ['tom', 'jason', 'chris'] Виведений результат на сторінці буде tomjasonchris

Тоді я хочу приєднати всі імена за допомогою коми, тому я змінюю код на

this.props.data.map(t => <span>t</span>).join(', ')

Однак виведений результат - це [Об'єкт], [Об'єкт], [Об'єкт].

Я не знаю, як інтерпретувати об'єкт, щоб стати реагуючими компонентами, які будуть надані. Будь-яка пропозиція?

Відповіді:


147

Просте рішення - використовувати reduce()без другого аргументу та без поширення попереднього результату:

class List extends React.Component {
  render() {
     <div>
        {this.props.data
          .map(t => <span>{t}</span>)
          .reduce((prev, curr) => [prev, ', ', curr])}
     </div>
  }
}

Без другого аргументу, reduce()буде починатися з індексом 1 , а не 0, а React абсолютно щасливий з вкладеними масивами.

Як сказано в коментарях, ви хочете використовувати це лише для масивів щонайменше з одним елементом, оскільки reduce()без другого аргументу викинете з порожнім масивом. Зазвичай це не повинно бути проблемою, оскільки ви хочете відобразити користувацьке повідомлення, яке говорить щось на зразок "це порожньо" для порожніх масивів.

Оновлення для Typescript

Ви можете використовувати це в Typescript (без небезпечного типу any) з React.ReactNodeпараметром типу на .map():

class List extends React.Component {
  render() {
     <div>
        {this.props.data
          .map<React.ReactNode>(t => <span>{t}</span>)
          .reduce((prev, curr) => [prev, ', ', curr])}
     </div>
  }
}

3
If the array is empty and no initialValue was provided, TypeError would be thrown.. тому він не працюватиме для порожнього масиву.
Євген Кревенець

6
Зауважимо також, що це рекурсивно вкладає prevмасив. наприклад [1, 2, 3, 4]стає [[[1,",",2],",",3],",",4].
Тамлін

2
@Tamlyn: Ви вважаєте, що моя оригінальна згадка про вашу точку у відповіді ("React цілком задоволений вкладеними масивами") є достатньо зрозумілою, чи варто мені це детальніше розглянути?
Maarten ter Horst

1
Хтось змусив це рішення працювати з React + TypeScript?
Метт Делл

1
@Jstuff Я почав використовувати будь-який. .reduce((prev: JSX.Element, current: JSX.Element): any => [prev, (<span>&nbsp;</span>), current])
Метт Делл

26

Ви можете використовувати reduceдля об'єднання декількох елементів масиву:

React.createClass({
  render() {
     <div>
        this.props.data
        .map(t => <span>t</span>)
        .reduce((accu, elem) => {
            return accu === null ? [elem] : [...accu, ',', elem]
        }, null)
     </div>
  }
})

Це ініціалізує акумулятор з null, тому ми можемо обернути перший елемент у масив. Для кожного наступного елемента в масиві ми побудуємо новий масив, який містить усі попередні елементи за допомогою ...-operator, додаємо роздільник і потім наступний елемент.

Array.prototype.reduce ()


2
дякую, саме те, що мені було потрібно. Ви можете зняти нульову перевірку та потрійну, ініціалізуючи акумулятор як порожній масив
Ларрі

7
@Larry, якщо ви видалите нульовий чек і потрійний і передаєте [] як ініціалізатор, як уникнути провідної коми? ['a'].reduce((a, e) => [...a, ',', e],[])врожайність [",", "a"]. Пропуск жодного ініціалізатора не працює, якщо масив порожній, і в цьому випадку він повертає TypeError.
Хлопець

@Guy, ви абсолютно правильні - моя помилка полягала в тому, що я з'єднував значення з пробілами і пропускав порожній проміжок у виведеному виході
Ларрі

12

Оновлення за допомогою React 16: Тепер можна візуалізувати рядки безпосередньо, тому ви можете спростити код, видаливши всі непотрібні <span>теги.

const list = ({ data }) => data.reduce((prev, curr) => [ prev, ', ', curr ]);

8

Прийнята відповідь фактично повертає масив масивів, оскільки prevкожен раз буде масивом. React досить розумний, щоб зробити цю роботу, але чи схильний він створювати проблеми в майбутньому, такі як порушення алгоритму реагування на різні реакції при наданні ключів до кожного результату карти.

Нова React.Fragmentфункція дозволяє нам робити це легко зрозумілим чином без основних проблем.

class List extends React.Component {
  render() {
     <div>
        {this.props.data
          .map((t, index) => 
            <React.Fragment key={index}>
              <span> {t}</span> ,
            </React.Fragment>
          )
      </div>
  }
}

З React.Fragmentможна просто помістити сепаратор ,зовні з повертається HTML і реагувати не буде скаржитися.


2
Дивовижне рішення, але вам потрібно видалити кому для останнього елемента масиву
indapublic

Ви завжди можете скористатися поворотом та індексом для вирішення цього питання.
Jstuff

2
Ви можете видалити останню кому за допомогою цієї модифікації: <ключ React.Fragment = {index}> <span> {t} </span> {index === this.props.data.length - 1? '': ','} </React.Fragment>
JohnP

7

<span>{t}</span>ви повертаєте є об'єктом, а не рядки. Перевірте документи про реакцію https://facebook.github.io/react/docs/jsx-in-depth.html#the-transform

Використовуючи .join()на поверненому масиві з map, ви приєднуєтесь до масиву об’єктів.[object Object], ...

Ви можете помістити кому всередину, <span></span>щоб вона виводилася так, як вам потрібно.

  render() {
    return (
      <div>
        { this.props.data.map(
            (t,i) => <span>{t}{ this.props.data.length - 1 === i ? '' : ','} </span>
          )
        }
      </div>
    )
  }

зразок: https://jsbin.com/xomopahalo/edit?html,js,output


3

Мій варіант:

{this.props.data
    .map(item => <span>{item}</span>)
    .map((item, index) => [index > 0 && ', ', item ])}

2

Якщо я просто хочу надати масив компонентів, розділених комою, я зазвичай вважаю reduceзанадто багатослівним. Коротше рішення в таких випадках -

{arr.map((item, index) => (
  <Fragment key={item.id}>
    {index > 0 && ', '}
    <Item {...item} />
  </Fragment>
))}

{index > 0 && ', '} виведе кома, а потім пробіл перед усіма елементами масиву, крім першого.

Якщо ви хочете відокремити другий до останнього пункту і останній на що - то ще, скажімо , рядок ' and ', ви можете замінити {index > 0 && ', '}з

{index > 0 && index !== arr.length - 1 && ', '}
{index === arr.length - 1 && ' and '}

2

Використовуючи es6, ви можете зробити щось на кшталт:

const joinComponents = (accumulator, current) => [
  ...accumulator, 
  accumulator.length ? ', ' : '', 
  current
]

А потім запустіть:

listComponents
  .map(item => <span key={item.id}> {item.t} </span>)
  .reduce(joinComponents, [])

1

Використовуйте вкладений масив, щоб утримати "," зовні.

  <div>
      {this.props.data.map((element, index) => index == this.props.data.length - 1 ? <span key={index}>{element}</span> : [<span key={index}>{element}</span>, ", "])}
  </div>

Оптимізуйте його, зберігаючи дані для масиву та змінюючи останній елемент, а не перевіряючи, чи є останній його елемент весь час.

  let processedData = this.props.data.map((element, index) => [<span key={index}>{element}</span>, ", "])
  processedData [this.props.data.length - 1].pop()
  <div>
      {processedData}
  </div>

0

Це працювало для мене:

    {data.map( ( item, i ) => {
                  return (
                      <span key={i}>{item.propery}</span>
                    )
                  } ).reduce( ( prev, curr ) => [ prev, ', ', curr ] )}

0

Як згадував Pith , React 16 дозволяє вам використовувати рядки безпосередньо, тому загортання рядків у прольотні теги більше не потрібно. Спираючись на відповідь Маартена , якщо ви також хочете одразу розібратися з користувацьким повідомленням (і уникати помилки на порожній масив), ви можете вести операцію з потрійним, якщо заява про властивість довжини в масиві. Це могло виглядати приблизно так:

class List extends React.Component {
  const { data } = this.props;

  render() {
     <div>
        {data.length
          ? data.reduce((prev, curr) => [prev, ', ', curr])
          : 'No data in the array'
        }
     </div>
  }
}

0

Це повинно повернути плоский масив. Обробіть корпус s першим об'єктом, який не можна відібрати, надавши початковий порожній масив і відфільтрував непотрібну першу кому з результату

[{}, 'b', 'c'].reduce((prev, curr) => [...prev, ', ', curr], []).splice(1) // => [{}, 'b', 'c']

0

Чи я єдиний, хто думає, що тут у відповідях відбувається багато зайвого поширення та гніздування? Точки для того, щоб бути стислими, але це залишає двері відкритими для питань масштабу або Реагувати, змінюючи те, як вони поводяться з вкладеними масивами / фрагментами.

const joinJsxArray = (arr, joinWith) => {
  if (!arr || arr.length < 2) { return arr; }

  const out = [arr[0]];
  for (let i = 1; i < arr.length; i += 1) {
    out.push(joinWith, arr[i]);
  }
  return out;
};

// render()
<div>
  {joinJsxArray(this.props.data.map(t => <span>t</span>), ', ')}
</div>

Один масив, без гніздування. Жоден сексуальний метод також не пов'язаний, але якщо ви часто це робите, ви завжди можете додати його до прототипу масиву або обгорнути його у функції, яка приймає зворотний виклик відображення, а також робити це за один раз.


0
function YourComponent(props) {

  const criteria = [];

  if (something) {
    criteria.push(<strong>{ something }</strong>);
  }

  // join the jsx elements with `, `
  const elements = criteria.reduce((accu, elem) => {
    return accu === null ? [elem] : [...accu, ', ', elem]
  }, null);

  // render in a jsx friendly way
  return elements.map((el, index) => <React.Fragment key={ index }>{ el }</React.Fragment> );

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.