Harita ve birleştirme kullanarak tepki bileşenleri nasıl oluşturulur?


107

Dizge Dizisini gösterecek bir bileşenim var. Kod şuna benzer:

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

Mükemmel çalışıyor. yani eğer props.data = ['tom', 'jason', 'chris'], sayfada oluşturulan sonuç olacaktır tomjasonchris.

Ardından, virgül kullanarak tüm isimleri birleştirmek istiyorum, bu yüzden kodu şu şekilde değiştiriyorum:

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

Ancak, oluşturulan sonuç [Object], [Object], [Object].

Oluşturulacak tepki bileşenleri haline gelecek nesneyi nasıl yorumlayacağımı bilmiyorum. Herhangi bir öneri?

Yanıtlar:


158

Basit bir çözüm, reduce()ikinci argüman olmadan ve önceki sonucu yaymadan kullanmaktır :

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

İkinci argüman olmadan, reduce()olacak indeksi 1 başlayacak yerine 0, ve iç içe diziler derece mutlu olduğunu yanıt verin.

Yorumlarda söylendiği gibi, bunu yalnızca en az bir öğe içeren diziler için kullanmak istersiniz, çünkü reduce()ikinci bağımsız değişken olmadan boş bir dizi atar. Normalde bu bir sorun olmamalıdır, çünkü zaten boş diziler için 'bu boş' gibi bir şey söyleyen özel bir mesaj görüntülemek istiyorsunuz.

Typescript için güncelleme

Bunu Typescript'te (tip-güvenli olmayan any) bir React.ReactNodetip parametresi ile birlikte kullanabilirsiniz .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.. bu yüzden boş dizi için çalışmayacaktır.
Eugene Krevenets

6
Ayrıca bunun prevdiziyi özyinelemeli olarak iç içe geçirdiğini unutmayın . örneğin [1, 2, 3, 4]olur [[[1,",",2],",",3],",",4].
Tamlyn

2
@Tamlyn: Cevabınızda ('React, iç içe dizilerle mükemmel bir şekilde mutludur') fikrinizden bahsettiğim orijinal açıklamamın yeterince açık olduğunu mu düşünüyorsunuz yoksa bu konuyu detaylandırmalı mıyım?
Maarten ter Horst

1
Bu çözümü React + TypeScript ile çalıştıran var mı?
Matt Dell

1
@Jstuff Herhangi birini kullanmak zorunda kaldım. .reduce((prev: JSX.Element, current: JSX.Element): any => [prev, (<span>&nbsp;</span>), current])
Matt Dell

26

reduceBir dizinin birden çok öğesini birleştirmek için kullanabilirsiniz :

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

Bu, akümülatörü null ile başlatır, böylece bir dizideki ilk öğeyi kaydırabiliriz. Dizideki her bir takip eden eleman için, önceki tüm elemanları içeren yeni bir dizi oluşturuyoruz ...-operator.

Array.prototype.reduce ()


2
teşekkürler, tam olarak ihtiyacım olan şey. Akümülatörü boş bir dizi olarak başlatarak boş kontrolü ve üçlüyü kaldırabilirsiniz
Larry

7
@Larry null kontrolü ve üçlü kontrolü kaldırır ve başlatıcı olarak [] geçerseniz, baştaki virgülden nasıl kaçınırsınız? ['a'].reduce((a, e) => [...a, ',', e],[])verim [",", "a"]. Dizi boş olmadığı sürece başlatıcıyı iletmek işe yaramaz, bu durumda bir TypeError döndürür.
Guy

@Guy, kesinlikle haklısınız - benim hatam, değerleri boşluklarla birleştiriyor olmam ve oluşturulan çıktıdaki boş aralığı kaçırmamdı
Larry,

13

React 16 ile güncelleme: Artık dizeleri doğrudan işlemek mümkün, böylece tüm gereksiz <span>etiketleri kaldırarak kodunuzu basitleştirebilirsiniz .

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

9

Kabul edilen yanıt aslında bir dizi dizisi döndürür çünkü prevher seferinde bir dizi olacaktır. React, bunu çalıştıracak kadar akıllıdır, ancak gelecekte, haritanın her bir sonucuna anahtarlar verirken Reacts farklı algoritmasını kırmak gibi sorunlara neden olmaya meyillidir.

Yeni React.Fragmentözellik, bunu temel sorunlar olmadan anlaşılması kolay bir şekilde yapmamızı sağlıyor.

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

İle React.Fragmentayırıcıyı ,döndürülen HTML'nin dışına yerleştirebiliriz ve React şikayet etmez.


2
Harika bir çözüm, ancak dizinin son öğesi için virgül kaldırmanız gerekiyor
indapublic

Bunu çözmek için her zaman bir dönüş ve indeks kullanabilirsiniz.
Jstuff

2
Bu değişiklikle son virgülü kaldırabilirsiniz: <React.Fragment key = {index}> <span> {t} </span> {index === this.props.data.length - 1? '': ','} </React.Fragment>
JohnP

7

<span>{t}</span>Eğer dönüyor bir nesne değil, bir dizedir. Bununla ilgili tepki belgelerini kontrol edin https://facebook.github.io/react/docs/jsx-in-depth.html#the-transform

Gönderen .join()diziyi kullanarak , mapnesnelerin dizisine katılıyorsunuz.[object Object], ...

Virgülün içine <span></span>girerek istediğiniz şekilde işlenmesini sağlayabilirsiniz .

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

örnek: https://jsbin.com/xomopahalo/edit?html,js,output


3

Benim varyantım:

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

3

Yalnızca virgülle ayrılmış bir bileşen dizisi oluşturmak istersem, genellikle reduceçok ayrıntılı bulurum . Bu gibi durumlarda daha kısa bir çözüm

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

{index > 0 && ', '} bir virgül ve ardından birincisi hariç tüm dizi öğelerinin önünde bir boşluk oluşturacaktır.

Eğer ikinci-to-son öğeyi ve başka bir şey tarafından sonuncusu ayırmak istiyorsanız, dize demek ' and ', sen yerine {index > 0 && ', '}sahip

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

2

Es6 kullanarak şunun gibi bir şey yapabilirsiniz:

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

Ve sonra çalıştırın:

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

1

"," Öğesini dışarıda tutmak için iç içe diziyi kullanın.

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

Her zaman son öğesinin olup olmadığını kontrol etmek yerine verileri diziye kaydederek ve son öğeyi değiştirerek onu optimize edin.

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

0

Bu benim için çalıştı:

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

0

Pith tarafından belirtildiği gibi , React 16 dizeleri doğrudan kullanmanıza izin verir, böylece dizeleri span etiketlerine sarmak artık gerekli değildir. Maarten'in cevabına dayanarak , özel bir mesajla hemen ilgilenmek istiyorsanız (ve boş diziye bir hata atmaktan kaçınmak istiyorsanız), dizideki length özelliği üzerinde bir ternary if ifadesi ile operasyonu yönetebilirsiniz. Bu şuna benzer bir şeye benzeyebilir:

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

Bu, düz bir dizi döndürmelidir. İlk boş dizi sağlayarak ve sonuçtan gerekli olmayan ilk virgülü filtreleyerek yinelenemez ilk nesneyle durumu ele alın

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

0

Buradaki cevaplarda çok fazla gereksiz yayılma ve iç içe geçme olduğunu düşünen tek kişi ben miyim? Kısa olmak için noktalar, elbette, ancak bu kapıyı, iç içe diziler / Parçalarla nasıl başa çıktıklarını değiştiren ölçek veya Tepki sorunlarına açık bırakır.

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>

Tek dizi, yuvalama yok. Seksi yöntem zincirleme de yok, ancak kendinizi bunu sık sık yaparken bulursanız, bunu her zaman dizi prototipine ekleyebilir veya hepsini tek seferde yapmak için eşleme geri çağrısı alan bir işleve sarabilirsiniz.


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

}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.