Rendern von Reaktionskomponenten mithilfe von Map und Join

102

Ich habe eine Komponente, die Array of String anzeigen wird. Der Code sieht so aus.

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

Es funktioniert einwandfrei. dh wenn props.data = ['tom', 'jason', 'chris'] Das gerenderte Ergebnis auf der Seite wäre tomjasonchris

Dann möchte ich alle Namen mit Komma verbinden, also ändere ich den Code in

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

Das gerenderte Ergebnis ist jedoch [Objekt], [Objekt], [Objekt].

Ich weiß nicht, wie ich ein Objekt interpretieren soll, um zu zu rendernden Reaktionskomponenten zu werden. Irgendein Vorschlag ?

Xiaohe Dong
quelle

Antworten:

147

Eine einfache Lösung besteht darin, reduce()ohne zweites Argument und ohne das vorherige Ergebnis zu verbreiten:

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

Ohne zweites Argument reduce()wird bei Index 1 beginnt anstelle von 0, und Reagieren Sie vollkommen zufrieden mit verschachtelter Arrays ist.

Wie in den Kommentaren erwähnt, möchten Sie dies nur für Arrays mit mindestens einem Element verwenden, da reduce()ohne zweites Argument mit einem leeren Array geworfen wird. Normalerweise sollte dies kein Problem sein, da Sie für leere Arrays ohnehin eine benutzerdefinierte Nachricht mit dem Titel "Dies ist leer" anzeigen möchten.

Update für Typescript

Sie können dies in Typescript (ohne typunsicher any) mit einem React.ReactNodeTypparameter verwenden .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
quelle
3
If the array is empty and no initialValue was provided, TypeError would be thrown.. Daher funktioniert es nicht für leere Arrays.
Eugene Krevenets
6
Beachten Sie auch, dass dies das prevArray rekursiv verschachtelt. zB [1, 2, 3, 4]wird [[[1,",",2],",",3],",",4].
Tamlyn
2
@ Tamlyn: Denken Sie, dass meine ursprüngliche Erwähnung Ihres Punktes in der Antwort ("Reagieren ist mit verschachtelten Arrays vollkommen zufrieden") klar genug ist, oder sollte ich darauf näher eingehen?
Maarten ter Horst
1
Hat jemand diese Lösung mit React + TypeScript zum Laufen gebracht?
Matt Dell
1
@Jstuff Ich musste keine verwenden. .reduce((prev: JSX.Element, current: JSX.Element): any => [prev, (<span>&nbsp;</span>), current])
Matt Dell
26

Sie können reducemehrere Elemente eines Arrays kombinieren:

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

Dadurch wird der Akkumulator mit null initialisiert, sodass das erste Element in ein Array eingeschlossen werden kann. Für jedes folgende Element im Array erstellen wir ein neues Array, das alle vorherigen Elemente enthält. ...-operatorFügen Sie dazu das Trennzeichen und dann das nächste Element hinzu.

Array.prototype.reduce ()

devsnd
quelle
2
Danke, genau das, was ich brauchte. Sie können die Nullprüfung und ternäre entfernen, indem Sie den Akkumulator als leeres Array initialisieren
Larry
7
@Larry Wenn Sie die Nullprüfung und den ternären Wert entfernen und [] als Initialisierer übergeben, wie vermeiden Sie ein führendes Komma? ['a'].reduce((a, e) => [...a, ',', e],[])ergibt [",", "a"]. Das Übergeben eines Initialisierers funktioniert nur, wenn das Array leer ist. In diesem Fall wird ein TypeError zurückgegeben.
Guy
@ Guy du bist absolut richtig - mein Fehler war, dass ich Werte mit Leerzeichen verband und die leere Spanne in der gerenderten Ausgabe fehlte
Larry
12

Update mit React 16: Es ist jetzt möglich, Zeichenfolgen direkt zu rendern, sodass Sie Ihren Code vereinfachen können, indem Sie alle nutzlosen <span>Tags entfernen .

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

Die akzeptierte Antwort gibt tatsächlich ein Array von Arrays zurück, da preves sich jedes Mal um ein Array handelt. React ist intelligent genug, um dies zum Laufen zu bringen, kann jedoch in Zukunft Probleme verursachen, z. B. das Brechen des unterschiedlichen Reacts-Algorithmus, wenn Schlüssel für jedes Ergebnis der Karte vergeben werden.

Mit der neuen React.FragmentFunktion können wir dies auf leicht verständliche Weise ohne die zugrunde liegenden Probleme tun.

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

Mit können React.Fragmentwir einfach das Trennzeichen ,außerhalb des zurückgegebenen HTML platzieren und React wird sich nicht beschweren.

Jstuff
quelle
2
Tolle Lösung, aber Sie müssen das Komma für das letzte Element des Arrays entfernen
indapublic
Sie können immer einen Dreher und den Index verwenden, um das zu lösen.
Jstuff
2
Sie können das letzte Komma mit dieser Änderung entfernen: <React.Fragment key = {index}> <span> {t} </ span> {index === this.props.data.length - 1? '': ','} </React.Fragment>
JohnP
7

Das, was <span>{t}</span>Sie zurückgeben, ist ein Objekt, keine Zeichenfolge. Überprüfen Sie die Reaktionsdokumente dazu https://facebook.github.io/react/docs/jsx-in-depth.html#the-transform

Durch die Verwendung .join()des zurückgegebenen Arrays von verbinden mapSie das Array von Objekten.[object Object], ...

Sie können das Komma in das Zeichen setzen, <span></span>damit es so gerendert wird, wie Sie es möchten.

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

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

oobgam
quelle
3

Meine Variante:

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

Wenn ich nur ein durch Kommas getrenntes Array von Komponenten rendern möchte, finde ich das normalerweise reducezu ausführlich. Eine kürzere Lösung ist in solchen Fällen

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

{index > 0 && ', '} rendert ein Komma, gefolgt von einem Leerzeichen vor allen Array-Elementen mit Ausnahme des ersten.

Wenn Sie die zweite zu letzten Punkt und die letzte durch etwas anderes trennen wollen, sagen Sie die Zeichenfolge ' and ', können Sie ersetzen {index > 0 && ', '}mit

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

Mit es6 können Sie Folgendes tun:

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

Und dann laufen:

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

Verwenden Sie ein verschachteltes Array, um "," draußen zu halten.

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

Optimieren Sie es, indem Sie die Daten im Array speichern und das letzte Element ändern, anstatt ständig zu überprüfen, ob es das letzte Element ist.

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

Das hat bei mir funktioniert:

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

Wie von Pith erwähnt , können Sie in React 16 Zeichenfolgen direkt verwenden, sodass das Umschließen der Zeichenfolgen in span-Tags nicht mehr erforderlich ist. Aufbauend auf Maartens Antwort können Sie die Operation mit einer ternären if-Anweisung für die length-Eigenschaft des Arrays ausführen, wenn Sie auch sofort eine benutzerdefinierte Nachricht bearbeiten möchten (und vermeiden möchten, einen Fehler in ein leeres Array zu werfen). Das könnte ungefähr so ​​aussehen:

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

Dies sollte ein flaches Array zurückgeben. Behandeln Sie den Fall mit einem nicht iterierbaren ersten Objekt, indem Sie ein anfängliches leeres Array bereitstellen und das nicht benötigte erste Komma aus dem Ergebnis herausfiltern

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

Bin ich der einzige, der denkt, dass in den Antworten hier eine Menge unnötiger Ausbreitung und Verschachtelung vor sich geht? Sicherlich prägnant, aber es lässt die Tür offen für Skalierungs- oder Reaktionsprobleme, die den Umgang mit verschachtelten Arrays / Fragmenten ändern.

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>

Ein Array, keine Verschachtelung. Auch keine sexy Methodenverkettung, aber wenn Sie dies häufig tun, können Sie es jederzeit zum Array-Prototyp hinzufügen oder in eine Funktion einbinden, die auch einen Mapping-Rückruf benötigt, um alles auf einmal zu erledigen.

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