Wie würden Sie dieses Rendering-Rätsel in React lösen?

Avatar of Burke Holland
Burke Holland am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Willkommen, React-Liebhaber und Amateure wie ich! Ich habe heute ein Rätsel für Sie.

Nehmen wir an, Sie möchten eine Liste von Elementen in einer 2-Spalten-Struktur rendern. Jedes dieser Elemente ist eine separate Komponente. Nehmen wir zum Beispiel an, wir hätten eine Liste von Alben und möchten diese als Vollseiten-2-Spalten-Liste rendern. Jedes „Album“ ist eine React-Komponente.

Scroll-Rendering-Problem

Nun nehmen wir an, das CSS-Framework, das Sie verwenden, erfordert, dass Sie ein 2-Spalten-Layout wie dieses rendern…

<div class="columns">
  <div class="column"> Column 1 </div>
  <div class="column"> Column 2 </div>
<div class="columns">

Das bedeutet, um die Alben korrekt zu rendern, müssen Sie einen `columns`-Div-Tag öffnen, zwei Alben rendern, dann den Tag schließen. Das tun Sie immer wieder, bis alle Alben gerendert wurden.

Ich habe es gelöst, indem ich die Menge in Blöcke zerlegt und bei jedem zweiten Album bedingt in einer separaten Renderfunktion gerendert habe. Diese Renderfunktion wird nur für jedes zweite Element aufgerufen.

class App extends Component { 
  state = {albums: [] }

  async componentDidMount() { 
    let data = Array.from(await GetAlbums());
    this.setState({ albums: data } ); 
  } 

  render() { 
    return (
      <section className="section">
        {this.state.albums.map((album, index) => {
          // use the modulus operator to determine even items return index % 2 ?
          this.renderAlbums(index) : '';
        })}
      </section> 
    )
  }

  renderAlbums(index) { 
    // two albums at a time - the current and previous item
    let albums = [this.state.albums[index - 1], this.state.albums[index]]; 
    
    return (
      <div className="columns" key={index}>
        {albums.map(album => {
          return (
            <Album album={album} />
          );
        })}
      </div>
    );
  }
}

Vollständiges Projekt anzeigen

Eine andere Möglichkeit wäre, das Album-Array in ein zweidimensionales Array aufzuteilen und darüber zu iterieren. Der erste hervorgehobene Block unten teilt das Array auf. Der zweite ist die stark vereinfachte Rendering-Logik.

class App extends Component { 
  state = {albums: []}
  
  async componentDidMount() { 
    let data = Array.from(await GetAlbums());
    
    // split the original array into a collection of two item sets
    data.forEach((item, index) => {
      if (index % 2) {
        albums.push([data[index - 1], data[index]]);
      }
    });

    this.setState({
      albums: albums
    });
  } 

  render() {
    return ( 
      <section className="section">
        {this.state.albums.map((album, index) => { 
          return ( 
            <div className="columns">
              <Album album={album[0]}></Album> 
              <Album album={album[1]}></Album> 
            </div> 
          ) 
        })}
      </section> 
    ) 
  } 
}

Vollständiges Projekt anzeigen

Das bereinigt das JSX ziemlich, aber jetzt gebe ich die `Album`-Komponente redundant ein, was sich einfach falsch anfühlt.

Sarah Drasner wies mich darauf hin, dass ich eines der wichtigeren Szenarien hier noch nicht einmal berücksichtigt hatte, nämlich das Szenario „unbekannte Unterseite“.

Unbekannte Unterseite

Beide meiner obigen Lösungen gehen davon aus, dass der vom Fetch erhaltene Ergebnissatz final ist. Aber was ist, wenn er es nicht ist?

Was ist, wenn wir Daten von einem Server streamen (im RxJs-Stil) und nicht wissen, wie oft wir einen Ergebnissatz erhalten, und nicht wissen, wie viele Elemente in einem bestimmten Satz enthalten sein werden. Das kompliziert die Dinge erheblich und zerstört die vorgeschlagenen Lösungen vollständig. Tatsächlich können wir sagen, dass keine dieser Lösungen ideal ist, da sie nicht für diesen Anwendungsfall skalieren.

Ich denke, die absolut einfachste Lösung hier wäre, dies im CSS zu beheben. Lassen Sie das CSS sich um das Layout kümmern, wie es Gott bestimmt hat. Ich halte es immer noch für wichtig, sich anzusehen, wie man das mit JSX macht, denn es gibt Leute, die jeden Tag mit solchen Schikanen in realen Anwendungen zu tun haben. Die Anforderungen sind nicht immer das, was wir wollen.

Wie würden Sie es machen?

Meine Frage ist genau das – wie würden Sie das machen? Gibt es einen saubereren, effizienteren Weg? Wie kann das so gemacht werden, dass es mit einer unbekannten Unterseite skaliert? Neugierige Geister (insbesondere meine) würden es gerne wissen.