React-window: Utilisation de l'élément de table HTML

Créé le 21 sept. 2018  ·  34Commentaires  ·  Source: bvaughn/react-window

Avez-vous un exemple d'utilisation de cela avec des tableaux? J'essaie de le faire fonctionner, mais je soupçonne fortement que cela ne fonctionne pas ou que je fais quelque chose de très mal.
J'ai mis le externalTagName à "table" et le innerTagName à "tbody", mais je n'obtiens aucun défilement.

Voici mon code, je ne sais pas si cela aide (les éléments sont une liste d'objets):

 <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

Commentaire le plus utile

@pupudu J'applaudis vos efforts open source et j'ai parcouru votre code de manière approfondie avant de décider d'essayer de m'attaquer à cela par moi-même. Je voulais juste la chose super simple que les autres recherchaient: cette bibliothèque mais avec des éléments de table.

Il s'est avéré que j'ai pu réussir cela avec un tout petit peu de code et beaucoup de créativité. Le code est suffisamment simple pour que n'importe qui puisse copier / coller et étendre à son goût. @bvaughn après avoir compris le comment, ce serait assez trivial à ajouter à la bibliothèque si vous le souhaitez, mais aussi assez trivial à remplacer si vous savez comment.

Le court de celui -

  • Capturez le style "haut" de la première ligne après un rendu
  • Stockez cette valeur dans React.Context afin que nous puissions la transmettre
  • Appliquez la valeur à un composant de table qui sera ce qui sera déplacé, au lieu des lignes elles-mêmes
  • Ajoutez des emplacements supplémentaires pour des éléments tels que les en-têtes et les pieds de page.
  • L'ergonomie de la bibliothèque ne permet pas de faire circuler les choses proprement, donc React.Context est le héros pour surmonter la communication entre composants.

Sandbox de code de travail: https://codesandbox.io/s/react-window-with-table-elements-d861o

Code de référence

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')
)

Tous les 34 commentaires

Je ne pense pas que cela fonctionnera vraiment. À ma connaissance, HTMLTableElement ne prend pas vraiment en charge le débordement de la manière dont un composant de fenêtrage aurait besoin. Vous _pourriez_ changer le style en display: block mais je ne pense toujours pas que cela fonctionnerait bien. ( Voici un exemple .)

Pourquoi avez-vous besoin d'une table? Je pense que vous pouvez obtenir la même disposition de colonne avec des blocs en ligne ou des dispositions flexibles.

Quelques autres choses:

  • Vous ne spécifiez pas de paramètre require width à List
  • Vous n'avez pas besoin d'ajouter le key à vos lignes
  • itemSize peut juste être 30 plutôt que () => 30 (il fonctionnera légèrement mieux)

Merci pour la réponse rapide.
Nous avons déjà discuté de sa réécriture en divs, mais comme il s'agit d'un composant déjà assez volumineux, nous voulions simplement nous en assurer avant de commencer cette réécriture. J'ai vu d'autres commentaires sur rc-table, alors j'ai pensé que cela était peut-être censé fonctionner.
Quoi qu'il en soit, ne vous inquiétez pas de la réécriture, ça devrait en valoir la peine je pense.

J'ai reçu des avertissements React concernant les clés sans la clé sur la ligne, ce qui était également déroutant pour moi compte tenu de la définition d'un itemKey, donc je ne sais pas ce qui se passe là-bas.

Merci

Einar

Le 21 septembre 2018, à 16h52, Brian Vaughn [email protected] a écrit:

Je ne pense pas que cela fonctionnera vraiment. À ma connaissance, HTMLTableElement ne prend pas vraiment en charge le débordement de la manière dont un composant de fenêtrage aurait besoin. Vous pouvez changer le style pour afficher: bloquer mais je ne pense toujours pas que cela fonctionnerait bien.

Pourquoi avez-vous besoin d'une table? Je pense que vous pouvez obtenir la même disposition de colonne avec des blocs en ligne ou des dispositions flexibles.

Quelques autres choses:

Vous ne spécifiez pas une largeur de paramètre requise pour la liste
Vous n'avez pas besoin d'ajouter la clé à vos lignes
itemSize peut juste être 30 plutôt que () => 30 (il fonctionnera légèrement mieux)
-
Vous recevez ceci parce que vous avez créé le fil de discussion.
Répondez directement à cet e-mail, affichez-le sur GitHub ou désactivez le fil de discussion.

J'ai reçu des avertissements React concernant les clés sans la clé sur la ligne, ce qui était également déroutant pour moi compte tenu de la définition d'un itemKey, donc je ne sais pas ce qui se passe là-bas.

Ce n'est pas prévu. Pouvez-vous partager un cas de reproduction avec moi (via Code Sandbox)?

Ooh, désolé. Je devrais ajouter que cela ne serait attendu que si votre fonction itemKey retournait undefined pour certains éléments - auquel cas le correctif devrait concerner cette fonction.

@bvaughn Je ne trouve rien de spécifique sur le fait que la prise en charge des débordements soit un facteur limitant. Curieusement, j'aimerais savoir ce que cela pourrait être. Je comprends certainement que la prise en charge de la fonctionnalité de base pour les deux peut être fastidieuse, mais il semble que l'élément de liste de base est tout ce qui est hors du choix de l'implémenteur pour le moment. Parlant pour moi-même et pour tous ceux qui pourraient utiliser react-window avec des scénarios d'en-tête fixes, ce serait bien d'obtenir le support tabulaire inhérent intégré dans les mises en page de table. L'utilisation de display: table prend "un" certain temps de développement et est une mise en page unique pour les applications environnantes qui pourraient par hasard être construites à l'aide de tables. Je pourrais toujours modifier le débordement ou d'autres propriétés via css (je le fais de toute façon dans une implémentation actuelle). Un accessoire elementType est-il hors de question?

@bvaughn Je ne trouve rien de spécifique sur le fait que la prise en charge des débordements soit un facteur limitant.

Peut-être que je n'ai pas utilisé le meilleur phrasé - mais si vous regardez le bac à sable auquel j'ai lié (

Voici un autre exemple qui n'est que du HTML: lx65871p69

Le HTMLSelectElement (le <tbody> ) ignore les styles comme height et overflow - ce qui le rend inutile pour le fenêtrage. Vous pouvez "corriger" cela en changeant son mode d'affichage ( display: block ) mais cela va à l'encontre de l'objectif d'utiliser un HTMLTableElement car il rompt le comportement de dimensionnement des colonnes.

Ce comportement de dimensionnement de colonne est interrompu au départ pour le cas d'utilisation de fenêtrage de toute façon, car la taille dépend du contenu dans les colonnes (qui changerait lorsque l'utilisateur fait défiler).

mais il semble seulement que l'élément de liste de base est tout ce qui est hors du choix de l'implémenteur pour le moment ... Une prop elementType est-elle hors de question?

Vous pouvez déjà spécifier le type de balise si vous le souhaitez vraiment ( 2j0z718mwy ) même si je ne vois pas ce que cela vous rapporte.

Parlant pour moi-même et pour tous ceux qui pourraient utiliser react-window avec des scénarios d'en-tête fixes, ce serait bien d'obtenir le support tabulaire inhérent intégré dans les mises en page de table.

L'utilisation d'un HTMLTableElement serait problématique pour les raisons que j'ai expliquées ci-dessus. Si vous voulez une mise en page tabulaire / grille avec un en-tête fixe, vous pouvez le faire en utilisant l'un des composants de liste standard :

Pourquoi avez-vous besoin d'une table? Je pense que vous pouvez obtenir la même disposition de colonne avec des blocs en ligne ou des dispositions flexibles.

Peut-être qu'une structure de table est nécessaire pour une certaine accessibilité (comme une meilleure lecture d'écran par rapport aux divs non sémantiques)? Je devine ici.

Dans ce cas, j'irais avec le remplacement du style d'affichage CSS et de tout autre style inhérent aux éléments de tableau.

Mon expérience dans la construction d'un composant de table complexe (avec des en-têtes fixes / épinglés de haut et de gauche, une structure colspan / rowspan alambiquée et un modèle de données pour décrire la structure derrière) il y a environ neuf ans (alors nous n'avions aucun React, aucun problème d'accessibilité, et autant que je me souvienne pas de virtualisation) est que mon collègue qui était responsable de ce composant a essayé de l'implémenter dans des éléments de tableau appropriés, a eu du mal à forcer la mise en page à se comporter de certaines manières, puis a décidé d'implémenter la mise en page du tableau en JavaScript pur à partir de divs absolument positionnés et dimensionnés. Cette solution a parfaitement fonctionné: pas de sauts de mise en page étranges, un défilement fluide (pas par ligne / par colonne), etc.

Peut-être qu'une structure de table est nécessaire pour une certaine accessibilité (comme une meilleure lecture d'écran par rapport aux divs non sémantiques)? Je devine ici.

Les rôles Aria peuvent être utilisés pour cela 😄

Super minutieux, merci. Vous venez de m'éviter de descendre dans le terrier du lapin pour essayer un prototype qui résoudrait un problème uniquement pour en créer d'autres.

salut
J'ai essayé de trouver un fil existant au lieu d'en créer un nouveau. J'espère que je suis au bon endroit ...

Existe-t-il un moyen d'avoir une sorte de "colspan" pour fusionner deux cellules pour FixedSizeGrid (serait probablement plus logique dans mon contexte)?
Cette question s'appuie sur une autre discussion entamée ici , mais pour avoir plus de sens, je préfère poster ici.

Existe-t-il un moyen d'avoir une sorte de "colspan" pour fusionner deux cellules pour FixedSizeGrid (aurait probablement plus de sens dans mon contexte)?

Non, ce n'est pas pris en charge.

@einarq @BlaineBradbury @sompylasar Au cas où vous seriez toujours intéressé, j'ai réussi à obtenir un travail de fenêtrage avec des balises de table. En fait, j'ai fini par créer une fenêtre de réaction enveloppant la bibliothèque. Vous pouvez voir une démo en direct ici: https://window-table.netlify.com/

La raison pour laquelle je voulais les balises de table était principalement pour le style. Styliser une table html régulière est assez facile avec tw-bootstrap, et je voulais donc faire la même chose avec une table fenêtrée.

Je suis tenté de marquer et de demander à Brian ce qu'il pense de cette implémentation (puisqu'il a mentionné que les balises de table html ne fonctionneraient pas). Mais il m'a déjà suffisamment aidé. Je vous laisse donc le soin.

Merci.

C'est une approche intéressante @pupudu. Je ne sais pas s'il y aurait des problèmes d'accessibilité pour avoir des balises <table> séparées pour les en-têtes et les lignes, mais bonne idée autrement.

J'essaye d'utiliser react-datasheet avec react-window , cela fonctionne essentiellement:

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

Il rend, le seul problème est Warning: validateDOMNesting(...): <tr> cannot appear as a child of <div>. index.js:1437 Warning: validateDOMNesting(...): <div> cannot appear as a child of <tbody>.

Hé, j'essaie cela avec React-Table et je remarque que je dois utiliser des div au lieu d'éléments de table. Le problème avec ceci est que les bibliothèques de style comme material / bootstrap reposent sur ces éléments. C'est donc beaucoup plus de travail de changer mon schéma de table en divs au lieu d'éléments de table. Est-il possible du tout d'utiliser des éléments de tableau pour des différences ou dois-je créer mon propre style maintenant?

@simkessy Sauf si vous l'avez déjà remarqué, vous devriez vérifier window-table. Il permet d'utiliser des balises de table html nous permettant de réutiliser des styles existants. Je dois admettre que la table n'est pas aussi avancée que react-table.
https://window-table.netlify.com/

@pupudu J'applaudis vos efforts open source et j'ai parcouru votre code de manière approfondie avant de décider d'essayer de m'attaquer à cela par moi-même. Je voulais juste la chose super simple que les autres recherchaient: cette bibliothèque mais avec des éléments de table.

Il s'est avéré que j'ai pu réussir cela avec un tout petit peu de code et beaucoup de créativité. Le code est suffisamment simple pour que n'importe qui puisse copier / coller et étendre à son goût. @bvaughn après avoir compris le comment, ce serait assez trivial à ajouter à la bibliothèque si vous le souhaitez, mais aussi assez trivial à remplacer si vous savez comment.

Le court de celui -

  • Capturez le style "haut" de la première ligne après un rendu
  • Stockez cette valeur dans React.Context afin que nous puissions la transmettre
  • Appliquez la valeur à un composant de table qui sera ce qui sera déplacé, au lieu des lignes elles-mêmes
  • Ajoutez des emplacements supplémentaires pour des éléments tels que les en-têtes et les pieds de page.
  • L'ergonomie de la bibliothèque ne permet pas de faire circuler les choses proprement, donc React.Context est le héros pour surmonter la communication entre composants.

Sandbox de code de travail: https://codesandbox.io/s/react-window-with-table-elements-d861o

Code de référence

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 J'adore l'approche. Idée vraiment intéressante pour utiliser Context pour y parvenir. J'aime le fait que vous ne rendez qu'un seul tableau, contrairement à mon approche où j'en rend deux, un pour l'en-tête et un pour le corps. Cela a causé beaucoup de confusion (je n'ai pas vu cela venir) chez les utilisateurs de la table de fenêtre.

J'essaierai d'utiliser ces idées dans window-table si cela ne vous dérange pas. Je pense que le plus grand défi serait de trouver comment redimensionner automatiquement les lignes en fonction du contenu (ce qui était au moins le plus grand défi de windo-table).

@jamesmfriedman qui semble très prometteur. @bvaughn avez-vous des inquiétudes concernant cette approche?

@jamesmfriedman, il semble que l'en-tête défile avec le contenu. Est-il possible de le rendre collant?

C'est en fait possible, et non spécifique à cette implémentation. Faites simplement quelques recherches sur Google autour des en-têtes collants avec des éléments de tableau HTML. Je me souviens vaguement que la façon de faire est en fait de rendre la position de l'élément TH collante.

@jamesmfriedman merci beaucoup pour votre exemple 👍 cela a beaucoup aidé @marink voici un exemple d'en-têtes collants https://codesandbox.io/s/react-window-with-table-elements-jj70e vous devez ajouter style={{ position: 'sticky', top: 0 }}> aux balises th

@jamesmfriedman Merci beaucoup pour l'exemple. Je vais chercher à l'utiliser. Mais je pense que l'utilisation de top: 0 et de position sticky au lieu de recalculer top et d'avoir une position absolue sur l'élément de table serait la voie à suivre. Parce que si votre position d'utilisation est collante pour obtenir des en-têtes collants sur les éléments, alors tout se fout sur un défilement rapide.

La ligne 79 serait donc <table style={{ top: 0, position: 'sticky', width: '100%' }}>

Cela résout également le problème de défilement qui se produit si vous continuez à faire défiler lorsque vous êtes en bas.

Une autre bonne raison d'utiliser les tableaux HTML est la prise en charge d'IE11. J'utilise FixedSizeList puis je le stylise avec CSS-grid. Cependant, lorsque vous devez ajouter d'autres fonctionnalités telles que des colonnes réactives, des colonnes de tailles différentes, etc., et que tout fonctionne avec IE11, vous vous retrouvez avec des quantités massives de code. Alors que les tableaux HTML fonctionnent par défaut sans code.

IE11 n'est plus quelque chose que vous devriez soutenir :)

Chaque développeur FE veut la mort d'IE11. Mais malheureusement, il y a des entreprises / personnes coincées avec cela, incapables de bouger pour des raisons que nous ne pouvons même pas imaginer. Espérons que ce n'est que quelques années à l'avance. Jusque-là, nous devrons soutenir IE11, que cela nous plaise ou non.

C'était aussi une pilule difficile à avaler pour moi.

@ olafur164 merci pour votre correction pour les en-têtes collants qui ne se cassent pas lors d'un défilement rapide. Savez-vous s'il existe un moyen de conserver l'apparence "collante" car maintenant, il fait défiler une ligne à la fois plutôt que de manière fluide?

Permettez-moi de partager mon expérience avec react-virtualized (même la page des problèmes de react-window ) et une bibliothèque d'interface utilisateur de matériaux appelée https://github.com/mbrn/ table-matière / issues / 891 # issueecomment -681986647. Il y a encore des problèmes tels que des problèmes de style et validateDOMNesting(...) avertissement

@jamesmfriedman merci beaucoup pour votre exemple 👍 cela a beaucoup aidé @marink voici un exemple d'en-têtes collants. vous devez ajouter style={{ position: 'sticky', top: 0 }}> aux balises th

Hey @JCofman , j'ai essayé votre correctif CSS mais cela ne fonctionne pas pour moi. Voici l'exemple de sandbox de code - https://codesandbox.io/s/react-window-with-table-elements-forked-huti6?file=/src/index.tsx : 514-542

Il y a deux problèmes -

  1. l'en-tête ne colle pas sur le dessus malgré les styles en place.
  2. l'en-tête clignote / scintille tout en défilant fortement vers le haut.
    [Étapes à suivre pour reproduire - faites défiler de 50% vers le bas puis faites défiler rapidement vers le haut et observez le mouvement de l'en-tête].

Merci d'avance :)

@jamesmfriedman J'adore l'approche. Idée vraiment intéressante pour utiliser Context pour y parvenir. J'aime le fait que vous ne rendez qu'un seul tableau, contrairement à mon approche où j'en rend deux, un pour l'en-tête et un pour le corps. Cela a causé beaucoup de confusion (je n'ai pas vu cela venir) chez les utilisateurs de la table de fenêtre.

J'essaierai d'utiliser ces idées dans window-table si cela ne vous dérange pas. Je pense que le plus grand défi serait de trouver comment redimensionner automatiquement les lignes en fonction du contenu (ce qui était au moins le plus grand défi de windo-table).

J'ai fini par trouver une solution très similaire car j'utilise display: grid avec display: contents pour les lignes, donc adhère fondamentalement à la contrainte des éléments table > tr .

Vous avez utilisé context + refs pour appeler une méthode interne, cependant, cela est totalement inutile car la même chose peut être accomplie en recherchant la valeur style.top du premier enfant:

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>
    )
  }
)

Le style prop est donné par la bibliothèque lorsqu'elle exécute createElement pour chaque ligne, et puisque votre composant Row n'affecte pas le style à tr , vous n'êtes pas les transmettre non plus.

Récemment, je me posais des questions sur quelque chose comme ça. Et si nous déplacions tout le corps de la table avec un remplissage en utilisant des pseudo éléments (après / avant) via les propriétés personnalisées css 🤔

Dans l'exemple en utilisant react-virtual be https://codesandbox.io/s/poc-react-virtual-table-jyz0m

J'avais absolument besoin d'une table pour pouvoir atteindre l'UX que je voulais et je n'étais pas satisfait des solutions de contournement pour le faire fonctionner avec react-window .

J'ai fini par extraire moi-même la logique de virutalisation vers un hook et l'implémenter avec deux tables, une pour les en-têtes et la seconde pour le contenu, similaire à ce qui a été publié ci-dessus. La différence dans mon approche est que la deuxième table avait toujours ses en-têtes dans le balisage mais visuellement cachés afin de rester accessible, alors que les en-têtes de la première étaient là juste pour les utilisateurs voyants et collés en position .. A fonctionné comme un charme, et le code était radicalement plus simple.

Juste le jeter là-bas au cas où quelqu'un voudrait essayer ça.

@piecyk C'est génial, très similaire à la façon dont virtuose est implémenté: https://virtuoso.dev/grouped-by-first-letter/

Je dois ajouter que le positionnement de l'ensemble du conteneur avec absolu / top est performant, mais en exécutant un audit de performance avec Chrome, je vois des problèmes CLS qui pourraient avoir un impact sur FPS. Je ne pense pas que l'impact soit perceptible, vous pouvez voir à quel point cela peut être rapide et fluide sur cet exemple: https://b6udg.csb.app/ (même s'il s'agit d'un conteneur de grande taille). Cependant, avec le rembourrage haut / bas, il n'y a PAS de CLS!

J'essaierai moi-même et mesurerai bientôt la différence.

@pupudu J'applaudis vos efforts open source et j'ai parcouru votre code de manière approfondie avant de décider d'essayer de m'attaquer à cela par moi-même. Je voulais juste la chose super simple que les autres recherchaient: cette bibliothèque mais avec des éléments de table.

Il s'est avéré que j'ai pu réussir cela avec un tout petit peu de code et beaucoup de créativité. Le code est suffisamment simple pour que n'importe qui puisse copier / coller et étendre à son goût. @bvaughn après avoir compris le comment, ce serait assez trivial à ajouter à la bibliothèque si vous le souhaitez, mais aussi assez trivial à remplacer si vous savez comment.

_Le short de celui-ci_:

  • Capturez le style "haut" de la première ligne après un rendu
  • Stockez cette valeur dans React.Context afin que nous puissions la transmettre
  • Appliquez la valeur à un composant de table qui sera ce qui sera déplacé, au lieu des lignes elles-mêmes
  • Ajoutez des emplacements supplémentaires pour des éléments tels que les en-têtes et les pieds de page.
  • L'ergonomie de la bibliothèque ne permet pas de faire circuler les choses proprement, donc React.Context est le héros pour surmonter la communication entre composants.

Sandbox de code de travail: https://codesandbox.io/s/react-window-with-table-elements-d861o

Code de référence

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')
)

Merci pour cela, quelqu'un a-t-il essayé (avec succès) de l'utiliser avec le composant InfiniteLoader ? ... J'ai essayé de le lancer mais j'ai des problèmes avec le "wrapper" interne, que je suppose que c'est parce que le chargeur infini transmet des accessoires inattendus à innerElementType.

Cette page vous a été utile?
0 / 5 - 0 notes