จะแสดงผลส่วนประกอบปฏิกิริยาโดยใช้แผนที่และเข้าร่วมได้อย่างไร


107

ฉันมีองค์ประกอบหนึ่งที่จะแสดง 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]อย่างไรก็ตามผลที่แสดงผลเป็น

ฉันไม่รู้วิธีตีความวัตถุให้กลายเป็นส่วนประกอบปฏิกิริยาที่จะแสดงผล ข้อเสนอแนะใด ๆ ?

คำตอบ:


158

วิธีแก้ปัญหาง่ายๆคือใช้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พารามิเตอร์ type บน.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.. ดังนั้นจึงใช้ไม่ได้กับอาร์เรย์ที่ว่างเปล่า
Eugene Krevenets

6
โปรดทราบด้วยว่าสิ่งนี้จะทำรังprevอาร์เรย์แบบวนซ้ำ เช่นจะกลายเป็น[1, 2, 3, 4] [[[1,",",2],",",3],",",4]
Tamlyn

2
@ Tamlyn: คุณคิดว่าการพูดถึงจุดเดิมของคุณในคำตอบ ('React is perfect happy with nested arrays') นั้นชัดเจนเพียงพอหรือฉันควรอธิบายอย่างละเอียดในเรื่องนี้?
Maarten ter Horst

1
ใครทำให้โซลูชันนี้ใช้ได้กับ React + TypeScript
Matt Dell

1
@Jstuff ฉันนึกขึ้นได้ว่าต้องใช้อะไรก็ได้ .reduce((prev: JSX.Element, current: JSX.Element): any => [prev, (<span>&nbsp;</span>), current])
Matt Dell

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
ขอบคุณสิ่งที่ฉันต้องการ คุณสามารถลบการตรวจสอบ null และ ternary โดยการเริ่มต้นตัวสะสมเป็นอาร์เรย์ว่าง
Larry

7
@ หากคุณลบการตรวจสอบ null และ ternary และส่ง [] เป็นตัวเริ่มต้นคุณจะหลีกเลี่ยงเครื่องหมายจุลภาคนำหน้าได้อย่างไร อัตราผลตอบแทน['a'].reduce((a, e) => [...a, ',', e],[]) [",", "a"]การส่ง initializer จะไม่ทำงานเว้นแต่อาร์เรย์จะว่างเปล่าซึ่งในกรณีนี้จะส่งคืน TypeError
Guy

@Guy คุณถูกต้องอย่างแน่นอน - ข้อผิดพลาดของฉันคือฉันกำลังรวมค่ากับช่องว่างและไม่มีช่วงว่างในเอาต์พุตที่แสดงผล
Larry

13

อัปเดตด้วย React 16:ตอนนี้คุณสามารถแสดงสตริงได้โดยตรงแล้วดังนั้นคุณจึงสามารถทำให้โค้ดของคุณง่ายขึ้นโดยการลบ<span>แท็กที่ไร้ประโยชน์ทั้งหมดออก

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

9

คำตอบที่ยอมรับจะส่งคืนอาร์เรย์อาร์เรย์จริง ๆ เนื่องจากprevจะเป็นอาร์เรย์ทุกครั้ง React ฉลาดพอที่จะทำงานนี้ได้ แต่มีแนวโน้มที่จะก่อให้เกิดปัญหาในอนาคตเช่นการทำลายอัลกอริทึมที่แตกต่างกันของ 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 ที่ส่งคืนและ React จะไม่บ่น


2
ทางออกที่ยอดเยี่ยม แต่คุณต้องลบเครื่องหมายจุลภาคสำหรับองค์ประกอบสุดท้ายของอาร์เรย์
indapublic

คุณสามารถใช้ turnery และดัชนีเพื่อแก้ปัญหานั้นได้เสมอ
Jstuff

2
คุณสามารถลบลูกน้ำสุดท้ายด้วยการแก้ไขนี้: <React.Fragment key = {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

ถ้าฉันแค่ต้องการสร้างอาร์เรย์ของคอมโพเนนต์ที่คั่นด้วยจุลภาคฉันมักจะพบว่าreduceverbose เกินไป วิธีแก้ปัญหาที่สั้นกว่าในกรณีเช่นนี้คือ

{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 อนุญาตให้คุณใช้สตริงได้โดยตรงดังนั้นการตัดสตริงในแท็กสแปนจึงไม่จำเป็นอีกต่อไป จากคำตอบของ Maartenหากคุณต้องการจัดการกับข้อความที่กำหนดเองทันที (และหลีกเลี่ยงการโยนข้อผิดพลาดในอาร์เรย์ว่าง) คุณสามารถดำเนินการด้วยคำสั่ง ternary 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>
  }
}

0

สิ่งนี้ควรส่งคืนอาร์เรย์แบบแบน จัดการเคสกับอ็อบเจ็กต์แรกที่ไม่สามารถทำซ้ำได้โดยการจัดเตรียมอาร์เรย์ว่างเริ่มต้นและกรองคอมมาตัวแรกที่ไม่ต้องการออกจากผลลัพธ์

[{}, '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> );

}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.