cara membuat komponen react dengan menggunakan map dan join

102

Saya memiliki satu komponen yang akan menampilkan Array of String. Kodenya terlihat seperti ini.

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

Ini bekerja dengan baik. yaitu jika props.data = ['tom', 'jason', 'chris'] Hasil yang diberikan di halaman ini adalah tomjasonchris

Lalu, saya ingin menggabungkan semua nama dengan menggunakan koma, jadi saya ubah kodenya menjadi

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

Namun, hasil yang diberikan adalah [Objek], [Objek], [Objek].

Saya tidak tahu bagaimana menafsirkan objek menjadi komponen reaksi yang akan diberikan. Ada saran ?

Xiaohe Dong
sumber

Jawaban:

147

Solusi sederhana adalah menggunakan reduce()tanpa argumen kedua dan tanpa menyebarkan hasil sebelumnya:

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

Tanpa argumen kedua, reduce()akan mulai dari indeks 1, bukan 0, dan React sangat puas dengan array bersarang.

Seperti yang dikatakan di komentar, Anda ingin menggunakan ini hanya untuk array dengan setidaknya satu item, karena reduce()tanpa argumen kedua akan muncul dengan array kosong. Biasanya ini seharusnya tidak menjadi masalah, karena Anda ingin menampilkan pesan khusus yang mengatakan sesuatu seperti 'ini kosong' untuk array kosong.

Pembaruan untuk Ketikan

Anda dapat menggunakan ini di Typecript (tanpa type-unsafe any) dengan React.ReactNodeparameter type di .map():

class List extends React.Component {
  render() {
     <div>
        {this.props.data
          .map<React.ReactNode>(t => <span>{t}</span>)
          .reduce((prev, curr) => [prev, ', ', curr])}
     </div>
  }
}
Maarten ter Horst
sumber
3
If the array is empty and no initialValue was provided, TypeError would be thrown.. jadi tidak akan berfungsi untuk array kosong.
Eugene Krevenets
6
Perhatikan juga bahwa ini secara rekursif menumpuk prevarray. misalnya [1, 2, 3, 4]menjadi [[[1,",",2],",",3],",",4].
Tamlyn
2
@Tamlyn: Apakah menurut Anda penyebutan asli saya tentang maksud Anda dalam jawaban ('Bereaksi sangat senang dengan array bersarang') cukup jelas atau haruskah saya menjelaskannya?
Maarten ter Horst
1
Adakah yang membuat solusi ini berfungsi dengan React + TypeScript?
Matt Dell
1
@Jstuff Aku harus menggunakan apapun. .reduce((prev: JSX.Element, current: JSX.Element): any => [prev, (<span>&nbsp;</span>), current])
Matt Dell
26

Anda dapat menggunakan reduceuntuk menggabungkan beberapa elemen dari sebuah array:

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

Ini menginisialisasi akumulator dengan null, jadi kita bisa membungkus item pertama dalam sebuah array. Untuk setiap elemen berikut dalam larik, kami membuat larik baru yang berisi semua elemen sebelumnya menggunakan ...-operator, tambahkan pemisah lalu elemen berikutnya.

Array.prototype.reduce ()

devsnd
sumber
2
terima kasih, apa yang saya butuhkan. Anda dapat menghapus pemeriksaan null dan terner dengan menginisialisasi akumulator sebagai array kosong
Larry
7
@ Larry jika Anda menghapus cek null dan ternary, dan meneruskan [] sebagai penginisialisasi, bagaimana Anda menghindari koma di depan? ['a'].reduce((a, e) => [...a, ',', e],[])hasil [",", "a"]. Meneruskan tidak ada penginisialisasi berfungsi kecuali jika array kosong, dalam hal ini ia mengembalikan TypeError.
Guy
@Guy Anda benar-benar benar - kesalahan saya adalah bahwa saya menggabungkan nilai dengan spasi, dan kehilangan rentang kosong dalam output yang diberikan
Larry
12

Perbarui dengan React 16: Sekarang mungkin untuk merender string secara langsung, sehingga Anda dapat menyederhanakan kode Anda dengan menghapus semua <span>tag yang tidak berguna .

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

Jawaban yang diterima sebenarnya mengembalikan array array karena prev akan menjadi larik setiap saat. React cukup pintar untuk membuat ini berfungsi, tetapi cenderung menyebabkan masalah di masa depan seperti merusak algoritma Reacts diffing saat memberikan kunci untuk setiap hasil peta.

Fitur baru React.Fragmentmemungkinkan kami melakukan ini dengan cara yang mudah dipahami tanpa masalah yang mendasarinya.

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

Dengan React.Fragmentmudahnya kita menempatkan pemisah di ,luar HTML yang dikembalikan dan React tidak akan mengeluh.

Jstuff
sumber
2
Solusi luar biasa, tetapi Anda perlu menghapus koma untuk elemen terakhir dari array
indapublic
Anda selalu dapat menggunakan turnery dan indeks untuk menyelesaikannya.
Jstuff
2
Anda dapat menghapus koma terakhir dengan modifikasi ini: <React.Fragment key = {index}> <span> {t} </span> {index === this.props.data.length - 1? '': ','} </React.Fragment>
JohnP
7

yang <span>{t}</span>Anda kembalikan adalah sebuah objek, bukan string. Periksa dokumen reaksi tentang itu https://facebook.github.io/react/docs/jsx-in-depth.html#the-transform

Dengan menggunakan .join()pada larik yang dikembalikan dari map, Anda bergabung dengan larik objek.[object Object], ...

Anda dapat meletakkan koma di dalamnya <span></span>sehingga ditampilkan seperti yang Anda inginkan.

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

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

oobgam
sumber
3

Variasi saya:

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

Jika saya hanya ingin membuat array komponen yang dipisahkan koma, saya biasanya merasa reduceterlalu bertele-tele. Solusi yang lebih singkat dalam kasus seperti itu adalah

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

{index > 0 && ', '} akan membuat koma diikuti dengan spasi di depan semua item array kecuali yang pertama.

Jika Anda ingin memisahkan item kedua ke terakhir dan terakhir dengan sesuatu yang lain, katakan stringnya ' and ', Anda dapat menggantinya {index > 0 && ', '}dengan

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

Menggunakan es6 Anda bisa melakukan sesuatu seperti:

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

Dan kemudian jalankan:

listComponents
  .map(item => <span key={item.id}> {item.t} </span>)
  .reduce(joinComponents, [])
Marco Antônio
sumber
1

Gunakan larik bersarang untuk menyimpan "," di luar.

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

Optimalkan dengan menyimpan data ke array dan mengubah elemen terakhir daripada memeriksa apakah elemen terakhirnya sepanjang waktu.

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

Ini berhasil untuk saya:

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

Seperti yang disebutkan oleh Pith , React 16 memungkinkan Anda menggunakan string secara langsung sehingga membungkus string dalam tag span tidak lagi diperlukan. Berdasarkan jawaban Maarten , jika Anda juga ingin menangani pesan khusus segera (dan menghindari kesalahan pada larik kosong), Anda dapat memimpin operasi dengan pernyataan terner if pada properti panjang pada larik. Itu bisa terlihat seperti ini:

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>
  }
}
Fredric Waadeland
sumber
0

Ini harus mengembalikan array datar. Tangani kasus s dengan objek pertama yang tidak dapat diulang dengan menyediakan larik kosong awal dan memfilter koma pertama yang tidak diperlukan dari hasilnya

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

Apakah saya satu-satunya yang berpikir ada banyak penyebaran yang tidak perlu terjadi dalam jawaban di sini? Poin untuk menjadi ringkas, tentu, tetapi membiarkan pintu terbuka untuk masalah skala atau Bereaksi mengubah cara mereka menangani array / Fragmen bersarang.

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>

Satu larik, tidak ada sarang. Tidak ada metode seksi yang merangkai juga, tetapi jika Anda sering melakukan ini, Anda selalu dapat menambahkannya ke prototipe array atau membungkusnya dalam fungsi yang mengambil callback pemetaan juga untuk melakukan semuanya sekaligus.

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

}
ilovett
sumber