React-window: HTML-Tabellenelement verwenden

Erstellt am 21. Sept. 2018  ·  34Kommentare  ·  Quelle: bvaughn/react-window

Haben Sie ein Beispiel für die Verwendung mit Tabellen? Ich versuche es zum Laufen zu bringen, aber ich habe den starken Verdacht, dass es entweder nicht funktioniert oder dass ich etwas sehr Falsches mache.
Ich habe den OuterTagName auf "Tabelle" und den InnerTagName auf "Tbody" gesetzt, aber ich bekomme kein Scrollen.

Hier ist mein Code, nicht sicher, ob er hilft (Elemente sind eine Liste von Objekten):

 <List
            outerRef={this._list}
            outerTagName="table"
            innerTagName="tbody"
            height={300}
            itemData={items}
            itemSize={() => 30}
            itemCount={items.length}
            itemKey={item => item.uniqueId}>
            {({index, style, data}) => {
              return (
                <tr style={style} key={index}>
                  <td>Item {index}</td>
                </tr>
              );
            }}
          </List>
💬 question

Hilfreichster Kommentar

@pupudu Ich begrüße Ihre Open-Source-Bemühungen und habe Ihren Code ausführlich

Es stellte sich heraus, dass ich dies mit einem sehr kleinen Stück Code und viel Kreativität erreichen konnte. Der Code ist so einfach, dass jeder in der Lage sein sollte, nach seinen Wünschen zu kopieren / einzufügen und zu erweitern. @bvaughn Nachdem Sie herausgefunden haben, wie, ist dies ziemlich trivial, um es der Bibliothek hinzuzufügen, wenn Sie möchten, aber auch ziemlich trivial, um es zu überschreiben, wenn Sie wissen, wie.

Das kurze daran :

  • Erfassen Sie den "oberen" Stil der ersten Zeile nach einem Rendering
  • Speichern Sie diesen Wert in React.Context, damit wir ihn weitergeben können
  • Wenden Sie den Wert auf eine Tabellenkomponente an, die anstelle der Zeilen selbst verschoben wird
  • Fügen Sie zusätzliche Steckplätze für Kopf- und Fußzeilen hinzu.
  • Die Ergonomie der Bibliothek erlaubt es nicht, Dinge ordentlich weiterzugeben, daher ist React.Context der Held, um die komponentenübergreifende Kommunikation zu überwinden.

Arbeitscode-Sandbox: https://codesandbox.io/s/react-window-with-table-elements-d861o

Code als Referenz

import React from 'react'
import { useState, useRef, useContext } from 'react'
import { FixedSizeList, FixedSizeListProps } from 'react-window'
import { render } from 'react-dom'

/** Context for cross component communication */
const VirtualTableContext = React.createContext<{
  top: number
  setTop: (top: number) => void
  header: React.ReactNode
  footer: React.ReactNode
}>({
  top: 0,
  setTop: (value: number) => {},
  header: <></>,
  footer: <></>,
})

/** The virtual table. It basically accepts all of the same params as the original FixedSizeList.*/
function VirtualTable({
  row,
  header,
  footer,
  ...rest
}: {
  header?: React.ReactNode
  footer?: React.ReactNode
  row: FixedSizeListProps['children']
} & Omit<FixedSizeListProps, 'children' | 'innerElementType'>) {
  const listRef = useRef<FixedSizeList | null>()
  const [top, setTop] = useState(0)

  return (
    <VirtualTableContext.Provider value={{ top, setTop, header, footer }}>
      <FixedSizeList
        {...rest}
        innerElementType={Inner}
        onItemsRendered={props => {
          const style =
            listRef.current &&
            // @ts-ignore private method access
            listRef.current._getItemStyle(props.overscanStartIndex)
          setTop((style && style.top) || 0)

          // Call the original callback
          rest.onItemsRendered && rest.onItemsRendered(props)
        }}
        ref={el => (listRef.current = el)}
      >
        {row}
      </FixedSizeList>
    </VirtualTableContext.Provider>
  )
}

/** The Row component. This should be a table row, and noted that we don't use the style that regular `react-window` examples pass in.*/
function Row({ index }: { index: number }) {
  return (
    <tr>
      {/** Make sure your table rows are the same height as what you passed into the list... */}
      <td style={{ height: '36px' }}>Row {index}</td>
      <td>Col 2</td>
      <td>Col 3</td>
      <td>Col 4</td>
    </tr>
  )
}

/**
 * The Inner component of the virtual list. This is the "Magic".
 * Capture what would have been the top elements position and apply it to the table.
 * Other than that, render an optional header and footer.
 **/
const Inner = React.forwardRef<HTMLDivElement, React.HTMLProps<HTMLDivElement>>(
  function Inner({ children, ...rest }, ref) {
    const { header, footer, top } = useContext(VirtualTableContext)
    return (
      <div {...rest} ref={ref}>
        <table style={{ top, position: 'absolute', width: '100%' }}>
          {header}
          <tbody>{children}</tbody>
          {footer}
        </table>
      </div>
    )
  }
)

/**
 * Render Our Example
 **/
render(
  <VirtualTable
    height={300}
    width="100%"
    itemCount={1000}
    itemSize={36}
    header={
      <thead>
        <tr>
          <th>Index</th>
          <th>Header 2</th>
          <th>Header 3</th>
          <th>Header 4</th>
        </tr>
      </thead>
    }
    row={Row}
    footer={
      <tfoot>
        <tr>
          <td>Footer 1</td>
          <td>Footer 2</td>
          <td>Footer 3</td>
          <td>Footer 4</td>
        </tr>
      </tfoot>
    }
  />,
  document.querySelector('main')
)

Alle 34 Kommentare

Ich denke nicht, dass das wirklich funktionieren wird. Meines Wissens unterstützt HTMLTableElement Überlauf nicht so, wie es eine Fensterkomponente benötigen würde. Sie könnten den Stil in display: block ändern, aber ich denke immer noch nicht, dass es ganz richtig funktionieren würde. ( Hier ist ein Beispiel .)

Warum brauchst du einen Tisch? Ich denke, Sie können das gleiche Spaltenlayout mit Inline-Blöcken oder Flex-Layouts erzielen.

Ein paar zusätzliche Dinge:

  • Sie geben keinen erforderlichen Parameter width bis List
  • Sie müssen die key zu Ihren Zeilen hinzufügen
  • itemSize kann nur 30 und nicht () => 30 (es wird etwas besser abschneiden)

Danke für die schnelle Antwort.
Wir haben bereits darüber gesprochen, es in divs umzuschreiben, aber da dies für eine vorhandene, ziemlich große Komponente ist, wollten wir nur sicherstellen, bevor wir mit dem Umschreiben beginnen. Ich habe einige andere Kommentare zu rc-table gesehen und dachte, dass es vielleicht funktionieren sollte.
Wie auch immer, macht das Umschreiben nichts aus, es sollte sich lohnen, denke ich.

Ich habe Reaktionswarnungen über Schlüssel ohne den Schlüssel in der Zeile erhalten, was mich auch verwirrte, wenn man bedenkt, dass ich einen itemKey setze, also nicht sicher, was dort vor sich geht.

Vielen Dank

Einar

Am 21. September 2018, um 16:52 Uhr, schrieb Brian Vaughn [email protected] :

Ich denke nicht, dass das wirklich funktionieren wird. Meines Wissens unterstützt HTMLTableElement einen Überlauf nicht so, wie es eine Fensterkomponente benötigen würde. Sie könnten den Stil ändern, um: block anzuzeigen, aber ich denke immer noch nicht, dass es ganz richtig funktionieren würde.

Warum brauchst du einen Tisch? Ich denke, Sie können das gleiche Spaltenlayout mit Inline-Blöcken oder Flex-Layouts erzielen.

Ein paar zusätzliche Dinge:

Sie geben keine erforderliche Parameterbreite für List an
Sie müssen den Schlüssel nicht zu Ihren Zeilen hinzufügen
itemSize kann nur 30 statt () => 30 sein (es wird etwas besser funktionieren)
- -
Sie erhalten dies, weil Sie den Thread verfasst haben.
Antworten Sie direkt auf diese E-Mail, zeigen Sie sie auf GitHub an oder schalten Sie den Thread stumm.

Ich habe Reaktionswarnungen über Schlüssel ohne den Schlüssel in der Zeile erhalten, was mich auch verwirrte, wenn man bedenkt, dass ich einen itemKey setze, also nicht sicher, was dort vor sich geht.

Dies wird nicht erwartet. Können Sie mir einen Repro-Fall mitteilen (über Code Sandbox)?

Oh, tut mir leid. Ich sollte hinzufügen, dass dies nur zu erwarten wäre, wenn Ihre itemKey -Funktion für einige Elemente undefined würde - in diesem Fall müsste die Korrektur für diese Funktion erfolgen.

@bvaughn Ich

@bvaughn Ich

Vielleicht habe ich nicht die beste Formulierung verwendet - aber wenn Sie sich die Sandbox ansehen, mit der ich verlinkt habe ( rrn61wkzwm ), werden Sie das unerwartete Verhalten sehen.

Hier ist ein weiteres Beispiel, das nur HTML ist: lx65871p69

Das HTMLSelectElement (das <tbody> ) ignoriert Stile wie height und overflow - was es für das Fenstern unbrauchbar macht. Sie können dies "beheben", indem Sie den Anzeigemodus ändern ( display: block ). Dies macht jedoch den Zweck der Verwendung von HTMLTableElement zunichte, da dadurch das Verhalten bei der Spaltengröße unterbrochen wird.

Dieses Verhalten bei der Spaltengröße ist für den Anwendungsfall der Fensterung zunächst ohnehin fehlerhaft, da die Größe vom Inhalt der Spalten abhängt (der sich ändern würde, wenn ein Benutzer einen Bildlauf durchführt).

aber es sieht nur so aus, als ob das Basislistenelement alles ist, was im Moment nicht vom Implementierer ausgewählt werden kann ... Kommt eine elementType-Requisite nicht in Frage?

Sie können den Tag-Typ bereits angeben, wenn Sie wirklich möchten ( 2j0z718mwy ), obwohl ich nicht sehe, was er Ihnen

Wenn ich für mich und alle spreche, die möglicherweise ein Reaktionsfenster mit festen Headerszenarien verwenden, wäre es schön, die in Tabellenlayouts integrierte tabellarische Unterstützung zu erhalten.

Die Verwendung eines HTMLTableElement wäre aus den oben erläuterten Gründen problematisch. Wenn Sie ein Tabellen- / Rasterlayout mit einer festen Überschrift wünschen, können Sie dies jedoch mit einer der regulären Listenkomponenten erreichen :

Warum brauchst du einen Tisch? Ich denke, Sie können das gleiche Spaltenlayout mit Inline-Blöcken oder Flex-Layouts erzielen.

Möglicherweise ist eine Tabellenstruktur für eine gewisse Zugänglichkeit erforderlich (z. B. besseres Lesen des Bildschirms im Vergleich zu nicht-semantischen Divs)? Ich vermute hier.

In diesem Fall würde ich den CSS-Anzeigestil und alle anderen Stile, die Tabellenelementen inhärent sind, überschreiben.

Meine Erfahrung beim Erstellen einer komplexen Tabellenkomponente (mit festen / angehefteten Überschriften von oben und links, verschlungener Colspan / Rowspan-Struktur und einem Datenmodell zur Beschreibung der dahinter stehenden Struktur) vor ungefähr neun Jahren (dann hatten wir keine Reaktion, keine Bedenken hinsichtlich der Barrierefreiheit). und soweit ich mich an keine Virtualisierung erinnere) hat mein Kollege, der für diese Komponente verantwortlich war, versucht, sie in richtigen Tabellenelementen zu implementieren, hatte Probleme damit, das Layout zu zwingen, sich auf bestimmte Weise zu verhalten, und entschied sich dann, das Tabellenlayout in reinem JavaScript von zu implementieren absolut positionierte und dimensionierte divs. Diese Lösung funktionierte perfekt: keine seltsamen Layoutsprünge, reibungsloses Scrollen (nicht pro Zeile / pro Spalte) usw.

Möglicherweise ist eine Tabellenstruktur für eine gewisse Zugänglichkeit erforderlich (z. B. besseres Lesen des Bildschirms im Vergleich zu nicht-semantischen Divs)? Ich vermute hier.

Hierfür können Arienrollen verwendet werden 😄

Super gründlich, danke. Sie haben mich gerade gerettet, als ich durch das Kaninchenloch ging, um einen Prototyp zu versuchen, der ein Problem nur lösen würde, um andere zu schaffen.

Hallo
Ich habe versucht, einen vorhandenen Thread zu finden, anstatt einen neuen zu erstellen. Ich hoffe ich bin an einem guten Ort ...

Gibt es eine Möglichkeit, eine Art "Colspan" zu haben, um zwei Zellen für FixedSizeGrid (wäre in meinem Kontext wahrscheinlich sinnvoller)?
Diese Frage stützt sich auf eine andere Diskussion, die hier begonnen

Gibt es eine Möglichkeit, eine Art "Colspan" zu haben, um zwei Zellen für FixedSizeGrid zusammenzuführen (wäre in meinem Kontext wahrscheinlich sinnvoller)?

Nein, dies wird nicht unterstützt.

@einarq @BlaineBradbury @sompylasar Falls Sie immer noch interessiert sind, habe ich es geschafft, https://window-table.netlify.com/

Der Grund, warum ich die Tabellen-Tags haben wollte, war hauptsächlich das Styling. Das Stylen einer normalen HTML-Tabelle ist mit Tw-Bootstrap ziemlich einfach, und daher wollte ich dasselbe mit einer Fenstertabelle tun.

Ich bin versucht, Brian zu markieren und zu fragen, was er von dieser Implementierung hält (da er erwähnt hat, dass HTML-Tabellen-Tags nicht funktionieren würden). Aber er hat mir schon genug geholfen. Also überlasse ich es euch.

Vielen Dank.

Das ist ein interessanter Ansatz @pupudu. Ich weiß nicht, ob es Bedenken hinsichtlich der Barrierefreiheit geben würde, wenn separate <table> -Tags für die Überschriften und Zeilen vorhanden wären, aber ansonsten eine gute Idee.

Ich versuche, react-datasheet mit react-window , es funktioniert im Grunde:

function sheetRenderer(props) {
  const Row = ({ index, style }) => (
    React.cloneElement(props.children[index], { style })
  );
  return (
    <table className={props.className}>
        <tbody>
          <FixedSizeList
              height={150}
              itemCount={1000}
              itemSize={35}
              width={300}
          >
            {Row}
          </FixedSizeList>
        </tbody>
    </table>
  )
}

export default function EntityTable(props: IEntityTableProps) {
  const initialGrid = useMemo(() => {
    return getTableGridFromCSV(props.entityListCSV);
  }, [props.entityListCSV]);
  const [modifiedGrid, updateModifiedGrid] = useImmer(initialGrid);
  return (
    <ReactDataSheet
      data={modifiedGrid}
      sheetRenderer={sheetRenderer}
      valueRenderer={cell => cell.value}
      onCellsChanged={changes => {
        updateModifiedGrid(draft => {
          changes.forEach(({ cell, row, col, value }) => {
            draft[row][col].value = value || '';
          });
        });
      }}
    />
  );
}

function getTableGridFromCSV(csv: string) {
  return csv.split('\n').map(row =>
    row.split(',').map((cell, index) => {
      if (index === 0) {
        return {
          value: cell,
          forceComponent: true,
          component: <button onClick={() => console.log(cell)}>{cell}</button>,
        };
      }
      if (index === 2) {
        return {
          value: cell,
        };
      }
      return {
        value: cell,
        component: <span>{cell}</span>,
      };
    }),
  );
}

Es wird gerendert, das einzige Problem ist Warning: validateDOMNesting(...): <tr> cannot appear as a child of <div>. index.js:1437 Warning: validateDOMNesting(...): <div> cannot appear as a child of <tbody>.

Hey, ich probiere das mit React-Table aus und stelle fest, dass ich divs anstelle von Tabellenelementen verwenden muss. Das Problem dabei ist, dass Styling-Bibliotheken wie Material / Bootstrap auf diesen Elementen basieren. Es ist also viel mehr Arbeit, mein Tabellenschema in divs anstatt in Tabellenelemente zu ändern. Ist es überhaupt möglich, Tabellenelemente für Unterschiede zu verwenden, oder muss ich jetzt mein eigenes Styling erstellen?

@simkessy Sofern Sie es nicht bereits bemerkt haben, sollten Sie die Fenstertabelle
https://window-table.netlify.com/

@pupudu Ich begrüße Ihre Open-Source-Bemühungen und habe Ihren Code ausführlich

Es stellte sich heraus, dass ich dies mit einem sehr kleinen Stück Code und viel Kreativität erreichen konnte. Der Code ist so einfach, dass jeder in der Lage sein sollte, nach seinen Wünschen zu kopieren / einzufügen und zu erweitern. @bvaughn Nachdem Sie herausgefunden haben, wie, ist dies ziemlich trivial, um es der Bibliothek hinzuzufügen, wenn Sie möchten, aber auch ziemlich trivial, um es zu überschreiben, wenn Sie wissen, wie.

Das kurze daran :

  • Erfassen Sie den "oberen" Stil der ersten Zeile nach einem Rendering
  • Speichern Sie diesen Wert in React.Context, damit wir ihn weitergeben können
  • Wenden Sie den Wert auf eine Tabellenkomponente an, die anstelle der Zeilen selbst verschoben wird
  • Fügen Sie zusätzliche Steckplätze für Kopf- und Fußzeilen hinzu.
  • Die Ergonomie der Bibliothek erlaubt es nicht, Dinge ordentlich weiterzugeben, daher ist React.Context der Held, um die komponentenübergreifende Kommunikation zu überwinden.

Arbeitscode-Sandbox: https://codesandbox.io/s/react-window-with-table-elements-d861o

Code als Referenz

import React from 'react'
import { useState, useRef, useContext } from 'react'
import { FixedSizeList, FixedSizeListProps } from 'react-window'
import { render } from 'react-dom'

/** Context for cross component communication */
const VirtualTableContext = React.createContext<{
  top: number
  setTop: (top: number) => void
  header: React.ReactNode
  footer: React.ReactNode
}>({
  top: 0,
  setTop: (value: number) => {},
  header: <></>,
  footer: <></>,
})

/** The virtual table. It basically accepts all of the same params as the original FixedSizeList.*/
function VirtualTable({
  row,
  header,
  footer,
  ...rest
}: {
  header?: React.ReactNode
  footer?: React.ReactNode
  row: FixedSizeListProps['children']
} & Omit<FixedSizeListProps, 'children' | 'innerElementType'>) {
  const listRef = useRef<FixedSizeList | null>()
  const [top, setTop] = useState(0)

  return (
    <VirtualTableContext.Provider value={{ top, setTop, header, footer }}>
      <FixedSizeList
        {...rest}
        innerElementType={Inner}
        onItemsRendered={props => {
          const style =
            listRef.current &&
            // @ts-ignore private method access
            listRef.current._getItemStyle(props.overscanStartIndex)
          setTop((style && style.top) || 0)

          // Call the original callback
          rest.onItemsRendered && rest.onItemsRendered(props)
        }}
        ref={el => (listRef.current = el)}
      >
        {row}
      </FixedSizeList>
    </VirtualTableContext.Provider>
  )
}

/** The Row component. This should be a table row, and noted that we don't use the style that regular `react-window` examples pass in.*/
function Row({ index }: { index: number }) {
  return (
    <tr>
      {/** Make sure your table rows are the same height as what you passed into the list... */}
      <td style={{ height: '36px' }}>Row {index}</td>
      <td>Col 2</td>
      <td>Col 3</td>
      <td>Col 4</td>
    </tr>
  )
}

/**
 * The Inner component of the virtual list. This is the "Magic".
 * Capture what would have been the top elements position and apply it to the table.
 * Other than that, render an optional header and footer.
 **/
const Inner = React.forwardRef<HTMLDivElement, React.HTMLProps<HTMLDivElement>>(
  function Inner({ children, ...rest }, ref) {
    const { header, footer, top } = useContext(VirtualTableContext)
    return (
      <div {...rest} ref={ref}>
        <table style={{ top, position: 'absolute', width: '100%' }}>
          {header}
          <tbody>{children}</tbody>
          {footer}
        </table>
      </div>
    )
  }
)

/**
 * Render Our Example
 **/
render(
  <VirtualTable
    height={300}
    width="100%"
    itemCount={1000}
    itemSize={36}
    header={
      <thead>
        <tr>
          <th>Index</th>
          <th>Header 2</th>
          <th>Header 3</th>
          <th>Header 4</th>
        </tr>
      </thead>
    }
    row={Row}
    footer={
      <tfoot>
        <tr>
          <td>Footer 1</td>
          <td>Footer 2</td>
          <td>Footer 3</td>
          <td>Footer 4</td>
        </tr>
      </tfoot>
    }
  />,
  document.querySelector('main')
)

@ Jamesmfriedman Ich liebe den Ansatz. Wirklich interessante Idee, um Context zu verwenden, um dies zu erreichen. Ich mag die Tatsache, dass Sie nur eine Tabelle rendern, anders als bei meinem Ansatz, bei dem ich zwei rendere, eine für den Header und eine für den Body. Dies hat bei Benutzern von Fenstertabellen viel Verwirrung gestiftet (ich habe das nicht gesehen).

Ich werde versuchen, diese Ideen in Fenstertabellen zu verwenden, wenn Sie nichts dagegen haben. Ich denke, die größte Herausforderung wäre es, herauszufinden, wie die Zeilen basierend auf dem Inhalt automatisch angepasst werden können (was zumindest die größte Herausforderung von windo-table war).

@ Jamesmfriedman , das sieht sehr vielversprechend aus. @bvaughn haben Sie Bedenken bezüglich dieses Ansatzes?

@jamesmfriedman Es scheint, als würde der Header zusammen mit dem Inhalt nach oben

Es ist tatsächlich möglich und nicht spezifisch für diese Implementierung. Googeln Sie einfach in klebrigen Headern mit HTML-Tabellenelementen. Ich erinnere mich vage, dass der Weg, dies zu tun, darin besteht, die Position des TH-Elements klebrig zu machen.

@jamesmfriedman vielen Dank für Ihr Beispiel 👍 es hat sehr geholfen @marink hier ist ein Beispiel für klebrige Header https://codesandbox.io/s/react-window-with-table-elements-jj70e Sie müssen style={{ position: 'sticky', top: 0 }}> hinzufügen th Tags

@jamesmfriedman Vielen Dank für das Beispiel. Ich werde mich darum kümmern, es zu benutzen. Aber ich denke, die Verwendung von top: 0 und position sticky anstelle einer Neuberechnung von top und die absolute Position auf dem Tabellenelement wäre der richtige Weg. Denn wenn Sie die Position klebrig verwenden, um klebrige Überschriften auf den th-Elementen zu erhalten, wird alles durch schnelles Scrollen durcheinander gebracht.

Zeile 79 wäre also <table style={{ top: 0, position: 'sticky', width: '100%' }}>

Dies behebt auch das Bildlaufproblem, das auftritt, wenn Sie weiter unten scrollen.

Ein weiterer guter Grund für die Verwendung von HTML-Tabellen ist die Unterstützung von IE11. Ich benutze FixedSizeList und gestalte es dann mit CSS-Grid. Wenn Sie jedoch andere Funktionen wie reaktionsfähige Spalten, Spalten mit unterschiedlichen Größen usw. hinzufügen und dafür sorgen müssen, dass alles mit IE11 zusammenarbeitet, erhalten Sie eine enorme Menge an Code. Während HTML-Tabellen standardmäßig ohne Code funktionieren.

IE11 sollten Sie nicht mehr unterstützen :)

Jeder einzelne FE-Entwickler will den Tod von IE11. Aber leider gibt es Unternehmen / Leute, die daran festhalten und aus Gründen, die wir uns nicht einmal vorstellen können, nicht in der Lage sind, den Schritt zu machen. Hoffentlich sind es nur noch wenige Jahre. Bis dahin müssen wir IE11 unterstützen, ob es uns gefällt oder nicht.

Es war auch für mich eine schwere Pille zu schlucken.

@ olafur164 danke für deine Korrektur für klebrige Header, die beim schnellen kaputt gehen . Wissen Sie, ob es eine Möglichkeit gibt, das "klebrige" Erscheinungsbild beizubehalten, da jetzt eine Reihe nach der anderen gescrollt wird, im Gegensatz zu einem reibungslosen Ablauf?

Lassen Sie mich meine Erfahrungen mit React-Virtualized (auch auf der React-Window- Seite mit Problemen) und einer Material-UI-Bibliothek namens Material-Table teilen, in der dasselbe Problem auftritt (Kombinationstabellenelemente mit Virtualisierung): https://github.com/mbrn/ Materialtabelle / Issues / 891 # Issuecomment -681986647. Es gibt immer noch einige Probleme wie Stylingprobleme und validateDOMNesting(...) Warnung.

@jamesmfriedman vielen Dank für Ihr Beispiel 👍 es hat sehr geholfen @marink hier ist ein Beispiel für klebrige Header. Sie müssen style={{ position: 'sticky', top: 0 }}> zu den th -Tags hinzufügen

Hey @JCofman , ich habe versucht, Ihr CSS zu reparieren, aber es funktioniert nicht für mich. Hier ist das Code-Sandbox-Beispiel: https://codesandbox.io/s/react-window-with-table-elements-forked-huti6?file=/src/index.tsx : 514-542

Es gibt zwei Probleme -

  1. Der Header klebt nicht an der Oberseite, obwohl Stile vorhanden sind.
  2. Der Header blinkt / flackert, während stark nach oben gescrollt wird.
    [Schritte zum Reproduzieren - 50% nach unten scrollen, dann schnell nach oben scrollen und die Header-Bewegung beobachten].

Danke im Voraus :)

@ Jamesmfriedman Ich liebe den Ansatz. Wirklich interessante Idee, um Context zu verwenden, um dies zu erreichen. Ich mag die Tatsache, dass Sie nur eine Tabelle rendern, anders als bei meinem Ansatz, bei dem ich zwei rendere, eine für den Header und eine für den Body. Dies hat bei Benutzern von Fenstertabellen viel Verwirrung gestiftet (ich habe das nicht gesehen).

Ich werde versuchen, diese Ideen in Fenstertabellen zu verwenden, wenn Sie nichts dagegen haben. Ich denke, die größte Herausforderung wäre es, herauszufinden, wie die Zeilen basierend auf dem Inhalt automatisch angepasst werden können (was zumindest die größte Herausforderung von windo-table war).

Am Ende fand ich eine sehr ähnliche Lösung, weil ich display: grid mit display: contents für die Zeilen verwende und mich daher grundsätzlich an die Einschränkung von table > tr Elementen halte.

Sie haben context + refs verwendet, um eine interne Methode aufzurufen. Dies ist jedoch völlig unnötig, da dies durch Nachschlagen des style.top-Werts des ersten untergeordneten Elements erreicht werden kann:

const Inner = React.forwardRef<HTMLDivElement, React.HTMLProps<HTMLDivElement>>(
  function Inner({ children, ...rest }, ref) {
    const { header, footer } = useContext(VirtualTableContext)
    const {style} = children[0].props;
    return (
      <div {...rest} ref={ref}>
        <table style={style}>
          {header}
          <tbody>{children}</tbody>
          {footer}
        </table>
      </div>
    )
  }
)

Die Stil-Requisite wird von der Bibliothek angegeben, wenn sie createElement für jede Zeile ausführt, und da Ihre bestimmte Row -Komponente style tr zuweist, sind Sie dies nicht sie auch weitergeben.

Vor kurzem habe ich mich über so etwas gewundert. Was wäre, wenn wir den gesamten Tabellenkörper mit Auffüllung unter Verwendung von Pseudoelementen (nach / vor) über benutzerdefinierte CSS-Eigenschaften verschieben würden 🤔

Beispiel: Verwenden Sie react-virtual https://codesandbox.io/s/poc-react-virtual-table-jyz0m

Ich brauchte unbedingt eine Tabelle, um die gewünschte UX zu erreichen, und war mit den Problemumgehungen nicht zufrieden, damit sie mit react-window funktioniert.

Am Ende habe ich die Virutalisierungslogik einfach selbst in einen Hook extrahiert und mit zwei Tabellen implementiert, eine für die Header und die zweite für den Inhalt, ähnlich wie oben beschrieben. Der Unterschied in meinem Ansatz besteht darin, dass die Überschriften der zweiten Tabelle noch im Markup enthalten waren, aber visuell verborgen waren, so dass sie zugänglich blieben, während die Überschriften der ersten Tabelle nur für sehende Benutzer da waren und in ihrer Position klebten Der Code war drastisch einfacher.

Wirf es einfach raus, falls jemand das versuchen will.

@piecyk Das ist großartig, sehr ähnlich wie Virtuose implementiert wird: https://virtuoso.dev/grouped-by-first-letter/

Ich muss hinzufügen, dass das Positionieren des gesamten Containers mit Absolut / Oben performant ist, aber wenn ich ein Perf-Audit mit Chrome durchführe, sehe ich CLS- Probleme, die sich auf FPS auswirken können. Ich glaube nicht, dass die Auswirkungen spürbar sind. In diesem Beispiel können Sie sehen, wie schnell und reibungslos sie sein können: https://b6udg.csb.app/ (obwohl es sich um einen großen Container handelt). Bei Polsterung oben / unten gibt es jedoch KEIN CLS!

Ich werde es selbst versuchen und den Unterschied bald messen.

@pupudu Ich begrüße Ihre Open-Source-Bemühungen und habe Ihren Code ausführlich

Es stellte sich heraus, dass ich dies mit einem sehr kleinen Stück Code und viel Kreativität erreichen konnte. Der Code ist so einfach, dass jeder in der Lage sein sollte, nach seinen Wünschen zu kopieren / einzufügen und zu erweitern. @bvaughn Nachdem Sie herausgefunden haben, wie, ist dies ziemlich trivial, um es der Bibliothek hinzuzufügen, wenn Sie möchten, aber auch ziemlich trivial, um es zu überschreiben, wenn Sie wissen, wie.

_The short of it_:

  • Erfassen Sie den "oberen" Stil der ersten Zeile nach einem Rendering
  • Speichern Sie diesen Wert in React.Context, damit wir ihn weitergeben können
  • Wenden Sie den Wert auf eine Tabellenkomponente an, die anstelle der Zeilen selbst verschoben wird
  • Fügen Sie zusätzliche Steckplätze für Kopf- und Fußzeilen hinzu.
  • Die Ergonomie der Bibliothek erlaubt es nicht, Dinge ordentlich weiterzugeben, daher ist React.Context der Held, um die komponentenübergreifende Kommunikation zu überwinden.

Arbeitscode-Sandbox: https://codesandbox.io/s/react-window-with-table-elements-d861o

Code als Referenz

import React from 'react'
import { useState, useRef, useContext } from 'react'
import { FixedSizeList, FixedSizeListProps } from 'react-window'
import { render } from 'react-dom'

/** Context for cross component communication */
const VirtualTableContext = React.createContext<{
  top: number
  setTop: (top: number) => void
  header: React.ReactNode
  footer: React.ReactNode
}>({
  top: 0,
  setTop: (value: number) => {},
  header: <></>,
  footer: <></>,
})

/** The virtual table. It basically accepts all of the same params as the original FixedSizeList.*/
function VirtualTable({
  row,
  header,
  footer,
  ...rest
}: {
  header?: React.ReactNode
  footer?: React.ReactNode
  row: FixedSizeListProps['children']
} & Omit<FixedSizeListProps, 'children' | 'innerElementType'>) {
  const listRef = useRef<FixedSizeList | null>()
  const [top, setTop] = useState(0)

  return (
    <VirtualTableContext.Provider value={{ top, setTop, header, footer }}>
      <FixedSizeList
        {...rest}
        innerElementType={Inner}
        onItemsRendered={props => {
          const style =
            listRef.current &&
            // @ts-ignore private method access
            listRef.current._getItemStyle(props.overscanStartIndex)
          setTop((style && style.top) || 0)

          // Call the original callback
          rest.onItemsRendered && rest.onItemsRendered(props)
        }}
        ref={el => (listRef.current = el)}
      >
        {row}
      </FixedSizeList>
    </VirtualTableContext.Provider>
  )
}

/** The Row component. This should be a table row, and noted that we don't use the style that regular `react-window` examples pass in.*/
function Row({ index }: { index: number }) {
  return (
    <tr>
      {/** Make sure your table rows are the same height as what you passed into the list... */}
      <td style={{ height: '36px' }}>Row {index}</td>
      <td>Col 2</td>
      <td>Col 3</td>
      <td>Col 4</td>
    </tr>
  )
}

/**
 * The Inner component of the virtual list. This is the "Magic".
 * Capture what would have been the top elements position and apply it to the table.
 * Other than that, render an optional header and footer.
 **/
const Inner = React.forwardRef<HTMLDivElement, React.HTMLProps<HTMLDivElement>>(
  function Inner({ children, ...rest }, ref) {
    const { header, footer, top } = useContext(VirtualTableContext)
    return (
      <div {...rest} ref={ref}>
        <table style={{ top, position: 'absolute', width: '100%' }}>
          {header}
          <tbody>{children}</tbody>
          {footer}
        </table>
      </div>
    )
  }
)

/**
 * Render Our Example
 **/
render(
  <VirtualTable
    height={300}
    width="100%"
    itemCount={1000}
    itemSize={36}
    header={
      <thead>
        <tr>
          <th>Index</th>
          <th>Header 2</th>
          <th>Header 3</th>
          <th>Header 4</th>
        </tr>
      </thead>
    }
    row={Row}
    footer={
      <tfoot>
        <tr>
          <td>Footer 1</td>
          <td>Footer 2</td>
          <td>Footer 3</td>
          <td>Footer 4</td>
        </tr>
      </tfoot>
    }
  />,
  document.querySelector('main')
)

Vielen Dank dafür, hat jemand (mit Erfolg) versucht, dies mit der InfiniteLoader -Komponente zu verwenden? ... Ich habe versucht, es herumzutreten, aber ich habe Probleme mit dem inneren "Wrapper", den ich habe Angenommen, der Infinite Loader übergibt unerwartete Requisiten an den innerElementType.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen