Подтвердить что ты не робот

Как визуализировать компоненты реакции с помощью карты и объединения

У меня есть один компонент, который будет отображать Array of String. Код выглядит следующим образом.

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(', ')

Однако рендеринг результата - [Object], [Object], [Object].

Я не знаю, как интерпретировать объект, чтобы стать реагирующим компонентом. Любое предложение?

4b9b3361

Ответ 1

Простым решением является использование 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() без второго аргумента будет выбрасывать с пустым массивом. Обычно это не должно быть проблемой, так как вы хотите отобразить пользовательское сообщение, говорящее что-то вроде 'this is empty' для пустых массивов в любом случае.

Обновление для 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>
  }
}

Ответ 2

Вы можете использовать reduce для объединения нескольких элементов массива:

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

Это инициализирует аккумулятор с нулем, поэтому мы можем обернуть первый элемент в массиве. Для каждого следующего элемента в массиве мы создаем новый массив, который содержит все предыдущие элементы, используя ...-operator, добавляем разделитель и затем следующий элемент.

Array.prototype.reduce()

Ответ 3

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

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

Ответ 4

возвращаемый <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

Ответ 5

Принятый ответ на самом деле возвращает массив массивов, потому что prev будет массивом каждый раз. Реакт достаточно умен, чтобы сделать эту работу, но он подвержен возникновению проблем в будущем, таких как нарушение алгоритма Reacts diffing при предоставлении ключей к каждому результату карты.

Новая функция 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, и React не будет жаловаться.

Ответ 6

Мой вариант:

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

Ответ 7

Используйте вложенный массив, чтобы сохранить "," снаружи.

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

Ответ 8

Если я просто хочу сделать через запятую массив компонентов, я обычно 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 '}

Ответ 9

Это сработало для меня:

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

Ответ 10

Как упомянул Pith, React 16 позволяет вам использовать строки напрямую, поэтому больше не требуется перенос строк в теги span. Основываясь на ответе Мартена, если вы также хотите иметь дело с настраиваемым сообщением сразу (и не генерировать ошибку для пустого массива), вы можете провести операцию с помощью троичного оператора if для свойства length в массиве. Это может выглядеть примерно так:

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

Ответ 11

Это должно вернуть плоский массив. Обработайте случай с не повторяемым первым объектом, предоставив исходный пустой массив и отфильтровав ненужную первую запятую из результата

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

Ответ 12

Используя es6, вы можете сделать что-то вроде:

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

А затем запустите:

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

Ответ 13

Я единственный, кто считает, что в ответах здесь много ненужного распространения и вложения? Конечно, это значит быть лаконичным, но это оставляет дверь открытой для вопросов масштаба или Реакции, изменяющих то, как они справляются с вложенными массивами/фрагментами.

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>

Один массив, без вложенности. Никакого сексуального метода также нет, но если вы обнаружите, что делаете это часто, вы всегда можете добавить его в прототип массива или обернуть его в функцию, которая также выполняет обратный вызов отображения, чтобы сделать все это за один раз.

Ответ 14

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

}