React-window: Usando el elemento de tabla html

Creado en 21 sept. 2018  ·  34Comentarios  ·  Fuente: bvaughn/react-window

¿Tiene un ejemplo de cómo usar esto con tablas? Estoy tratando de que funcione, pero tengo la fuerte sospecha de que no funciona o que estoy haciendo algo muy mal.
Establecí el ExternalTagName en "tabla" y el innerTagName en "tbody", pero no obtengo ningún desplazamiento.

Aquí está mi código, no estoy seguro de si ayuda (los elementos son una lista de objetos):

 <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

Comentario más útil

@pupudu Aplaudo sus esfuerzos de código abierto y

Resulta que pude lograr esto con un poco de código y mucha creatividad. El código es lo suficientemente simple como para que cualquiera pueda copiar / pegar y extender a su gusto. @bvaughn después de averiguar cómo, sería bastante trivial agregar esto a la biblioteca si lo desea, pero también bastante trivial de anular si sabe cómo.

En resumen :

  • Capture el estilo "superior" de la primera fila después de un renderizado
  • Almacene ese valor en React.Context para que podamos pasarlo
  • Aplicar el valor a un componente de la tabla que será lo que se mueva, en lugar de las filas en sí
  • Agregue espacios adicionales para cosas como encabezados y pies de página.
  • La ergonomía de la biblioteca no permite que las cosas se pasen ordenadamente, por lo que React.Context es el héroe para superar la comunicación entre componentes.

Sandbox de código de trabajo: https://codesandbox.io/s/react-window-with-table-elements-d861o

Código de referencia

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

Todos 34 comentarios

No creo que esto funcione realmente. Que yo sepa, HTMLTableElement realmente no admite el desbordamiento de la forma en que lo necesitaría un componente de ventana. _Podría_ cambiar el estilo a display: block pero todavía no creo que funcione bien. ( Aquí hay un ejemplo ).

¿Por qué necesitas una mesa? Creo que puede lograr el mismo diseño de columna con bloques en línea o diseños flexibles.

Un par de cosas adicionales:

  • No está especificando un parámetro obligatorio width a List
  • No necesita agregar key a sus filas
  • itemSize puede ser 30 lugar de () => 30 (funcionará un poco mejor)

Gracias por la rápida respuesta.
Ya hemos hablado de reescribirlo en divs, pero dado que esto es para un componente existente bastante grande, solo queríamos asegurarnos antes de comenzar esa reescritura. Vi algunos otros comentarios sobre rc-table, así que pensé que tal vez se suponía que debía funcionar.
De todos modos, no te preocupes por la reescritura, creo que debería valer la pena.

Recibí advertencias de React sobre claves sin la clave en la fila, lo que también fue confuso para mí considerando que estoy configurando un itemKey, así que no estoy seguro de lo que está sucediendo allí.

Gracias

Einar

El 21 de septiembre de 2018, a las 16:52, Brian Vaughn [email protected] escribió:

No creo que esto funcione realmente. Que yo sepa, HTMLTableElement realmente no admite el desbordamiento de la forma en que lo necesitaría un componente de ventana. Podría cambiar el estilo para mostrar: block pero todavía no creo que funcione bien.

¿Por qué necesitas una mesa? Creo que puede lograr el mismo diseño de columna con bloques en línea o diseños flexibles.

Un par de cosas adicionales:

No está especificando un ancho de parámetro obligatorio para la lista
No es necesario que agregue la clave a sus filas
itemSize puede ser 30 en lugar de () => 30 (funcionará un poco mejor)
-
Estás recibiendo esto porque eres el autor del hilo.
Responda a este correo electrónico directamente, véalo en GitHub o silencie el hilo.

Recibí advertencias de React sobre claves sin la clave en la fila, lo que también fue confuso para mí considerando que estoy configurando un itemKey, así que no estoy seguro de lo que está sucediendo allí.

No es de esperar. ¿Puedes compartir un caso de reproducción conmigo (a través de Code Sandbox)?

Oh, lo siento. Debo agregar que esto _sólo_ se esperaría si su función itemKey devolviera undefined para algunos elementos, en cuyo caso la solución debería ser para esa función.

@bvaughn No encuentro nada específico acerca de que el soporte de desbordamiento sea un factor limitante. Curiosamente, me gustaría saber qué podría ser. Ciertamente entiendo que admitir la funcionalidad principal para ambos puede ser tedioso, pero solo parece que el elemento de la lista base es todo lo que está fuera de la elección del implementador en este momento. Hablando por mí y por cualquiera que pueda estar usando react-window con escenarios de encabezado fijo, sería bueno obtener el soporte tabular inherente integrado en los diseños de tabla. El uso de display: table requiere "algo" de sobrecarga de desarrollo y es un diseño único para las aplicaciones circundantes que, de manera incidental, podrían construirse utilizando tablas. Siempre podría modificar el desbordamiento u otras propiedades a través de css (de todos modos, lo estoy haciendo en una implementación actual). ¿Está fuera de discusión un accesorio elementType?

@bvaughn No encuentro nada específico acerca de que el soporte de desbordamiento sea un factor limitante.

Quizás no utilicé la mejor redacción, pero si miras el Sandbox al que rrn61wkzwm ), verás el comportamiento inesperado.

Aquí hay otro ejemplo que es solo HTML: lx65871p69

El HTMLSelectElement (el <tbody> ) ignora estilos como height y overflow , lo que lo hace inútil para la creación de ventanas. Puede "arreglar" esto cambiando su modo de visualización ( display: block ) pero esto anula el propósito de usar un HTMLTableElement porque rompe el comportamiento del tamaño de la columna.

Ese comportamiento de tamaño de columna está roto para comenzar con el caso de uso de ventanas de todos modos, porque el tamaño depende del contenido de las columnas (que cambiaría a medida que un usuario se desplazara).

pero solo parece que el elemento de la lista base es todo lo que está fuera de la elección del implementador en este momento ... ¿Está fuera de discusión un prop elementType?

Ya puede especificar el tipo de etiqueta si realmente lo desea ( 2j0z718mwy ) aunque no veo qué le

Hablando por mí y por cualquiera que pueda estar usando react-window con escenarios de encabezado fijo, sería bueno obtener el soporte tabular inherente integrado en los diseños de tabla.

Usar un HTMLTableElement sería problemático por las razones que expliqué anteriormente. Sin embargo, si desea un diseño tabular / de cuadrícula con un encabezado fijo, puede lograrlo utilizando uno de los componentes de la lista normal: pk78pvwnkx

¿Por qué necesitas una mesa? Creo que puede lograr el mismo diseño de columna con bloques en línea o diseños flexibles.

¿Quizás se requiere una estructura de tabla para cierta accesibilidad (como, una mejor lectura de pantalla en comparación con divs no semánticos)? Supongo que aquí.

En este caso, optaría por anular el estilo de visualización de CSS y cualquier otro estilo que sea inherente a los elementos de la tabla.

Mi experiencia en la construcción de un componente de tabla complejo (con encabezados fijos / fijos desde la parte superior e izquierda, una estructura colspan / rowspan intrincada y un modelo de datos para describir la estructura detrás de él) hace unos nueve años (entonces no teníamos React, no teníamos problemas de accesibilidad y por lo que recuerdo sin virtualización) es que mi colega que era responsable de este componente intentó implementarlo en elementos de tabla adecuados, luchó para forzar el diseño a comportarse de ciertas maneras, y luego decidió implementar el diseño de la tabla en JavaScript puro desde divs absolutamente posicionados y dimensionados. Esa solución funcionó perfectamente: sin saltos de diseño extraños, desplazamiento suave (no por fila / por columna), etc.

¿Quizás se requiere una estructura de tabla para cierta accesibilidad (como, una mejor lectura de pantalla en comparación con divs no semánticos)? Supongo que aquí.

Los roles de Aria se pueden usar para esto 😄

Super completo, gracias. Acabas de salvarme de ir por la madriguera del conejo para intentar un prototipo que resolvería un problema solo para crear otros.

Hola
Intenté encontrar un hilo existente en lugar de crear uno nuevo. Espero estar en el buen lugar ...

¿Hay alguna manera de tener una especie de "colspan" para fusionar dos celdas por FixedSizeGrid (probablemente tendría más sentido en mi contexto)?
Esta pregunta se basa en otra discusión que comenzó aquí , pero para que tenga más sentido, prefiero publicar aquí.

¿Hay alguna manera de tener una especie de "colspan" para fusionar dos celdas para FixedSizeGrid (probablemente tendría más sentido en mi contexto)?

No. Esto no es compatible.

@einarq @BlaineBradbury @sompylasar En caso de que todavía esté interesado, me las arreglé para trabajar en ventanas con etiquetas de tabla. De hecho, terminé creando una ventana de reacción de envoltura de biblioteca. Puede ver una demostración en vivo aquí: https://window-table.netlify.com/

La razón por la que quería las etiquetas de mesa era principalmente para diseñar. Diseñar una tabla html normal es bastante fácil con tw-bootstrap y, por lo tanto, quería hacer lo mismo con una tabla con ventana.

Estoy tentado de etiquetar y preguntar a Brian qué piensa sobre esta implementación (ya que mencionó que las etiquetas de tabla html no funcionarían). Pero ya me ayudó lo suficiente. Así que se lo dejo a ustedes.

Gracias.

Ese es un enfoque interesante @pupudu. No sé si habría algún problema de accesibilidad por tener etiquetas <table> separadas para los encabezados y las filas, pero una buena idea de lo contrario.

Estoy tratando de usar react-datasheet con react-window , básicamente funciona:

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

Se renderiza, el único problema es Warning: validateDOMNesting(...): <tr> cannot appear as a child of <div>. index.js:1437 Warning: validateDOMNesting(...): <div> cannot appear as a child of <tbody>.

Oye, estoy probando esto con React-Table y me doy cuenta de que tengo que usar divs en lugar de elementos de la tabla. El problema con esto es que las bibliotecas de estilo como material / bootstrap se basan en esos elementos. Así que es mucho más trabajo cambiar el esquema de mi tabla a divs en lugar de elementos de tabla. ¿Es posible usar elementos de tabla para diferenciar o tengo que crear mi propio estilo ahora?

@simkessy A menos que ya lo hayas notado, deberías pagar window-table. Permite usar etiquetas de tabla html permitiéndonos reutilizar estilos existentes. Sin embargo, tengo que admitir que la tabla no es tan avanzada como la tabla de reacciones.
https://window-table.netlify.com/

@pupudu Aplaudo sus esfuerzos de código abierto y

Resulta que pude lograr esto con un poco de código y mucha creatividad. El código es lo suficientemente simple como para que cualquiera pueda copiar / pegar y extender a su gusto. @bvaughn después de averiguar cómo, sería bastante trivial agregar esto a la biblioteca si lo desea, pero también bastante trivial de anular si sabe cómo.

En resumen :

  • Capture el estilo "superior" de la primera fila después de un renderizado
  • Almacene ese valor en React.Context para que podamos pasarlo
  • Aplicar el valor a un componente de la tabla que será lo que se mueva, en lugar de las filas en sí
  • Agregue espacios adicionales para cosas como encabezados y pies de página.
  • La ergonomía de la biblioteca no permite que las cosas se pasen ordenadamente, por lo que React.Context es el héroe para superar la comunicación entre componentes.

Sandbox de código de trabajo: https://codesandbox.io/s/react-window-with-table-elements-d861o

Código de referencia

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 Me encanta el enfoque. Idea realmente interesante para usar Context para lograr esto. Me gusta el hecho de que solo renderiza una tabla, a diferencia de mi enfoque donde renderizo dos, una para el encabezado y otra para el cuerpo. Esto ha causado mucha confusión (no lo vi venir tbh) en los usuarios de la tabla de ventana.

Intentaré usar estas ideas en la mesa de la ventana si no le importa. Creo que el mayor desafío sería descubrir cómo ajustar automáticamente el tamaño de las filas en función del contenido (que fue al menos el mayor desafío de la tabla de ventanas).

@jamesmfriedman que parece muy prometedor. @bvaughn, ¿le preocupa este enfoque?

@jamesmfriedman parece que el encabezado se desplaza hacia arriba junto con el contenido. ¿Es posible hacerlo pegajoso?

De hecho, es posible y no específico de esta implementación. Simplemente busque en Google encabezados fijos con elementos de tabla HTML. Recuerdo vagamente que la forma de hacer esto es en realidad hacer que la posición del elemento TH sea pegajosa.

@jamesmfriedman muchas gracias por su ejemplo 👍 ayudó mucho @marink aquí hay un ejemplo de encabezados adhesivos https://codesandbox.io/s/react-window-with-table-elements-jj70e necesita agregar style={{ position: 'sticky', top: 0 }}> a las etiquetas th

@jamesmfriedman Muchas gracias por el ejemplo. Voy a considerar su uso. Pero creo que usar top: 0 y position sticky en lugar de recalcular top y tener la posición absoluta en el elemento de la tabla sería el camino a seguir. Porque si usa la posición pegajosa para obtener encabezados pegajosos en los elementos, entonces todo se arruina en el desplazamiento rápido.

Entonces la línea 79 sería <table style={{ top: 0, position: 'sticky', width: '100%' }}>

Eso también soluciona el problema de desplazamiento que es si continúa desplazándose cuando está en la parte inferior.

Otra buena razón para usar tablas HTML es la compatibilidad con IE11. Estoy usando FixedSizeList y luego le doy estilo con CSS-grid. Sin embargo, cuando necesita agregar otras características como columnas receptivas, columnas con diferentes tamaños, etc., y hacer que todo funcione junto con IE11, termina con cantidades masivas de código. Mientras que las tablas HTML funcionan por defecto sin código.

IE11 ya no es algo que debas admitir :)

Cada desarrollador de FE quiere la muerte de IE11. Pero desafortunadamente, hay empresas / personas que no pueden hacer el cambio debido a razones que ni siquiera podemos imaginar. Con suerte, solo quedan unos pocos años por delante. Hasta eso, necesitaremos ser compatible con IE11, nos guste o no.

También fue una pastilla difícil de tragar para mí.

@ olafur164 gracias por su corrección para los encabezados pegajosos que no se rompen en el desplazamiento rápido. ¿Sabes si hay una manera de mantener la apariencia "pegajosa", ya que ahora se desplaza una fila a la vez en lugar de sin problemas?

Permítanme compartir mi experiencia con react-virtualized (incluso su página de problemas de la ventana de material llamada https://github.com/mbrn/ material-table / issues / 891 # issuecomment -681986647. Todavía hay algunos problemas como problemas de estilo y advertencia validateDOMNesting(...) .

@jamesmfriedman muchas gracias por su ejemplo 👍 ayudó mucho @marink aquí es un ejemplo de encabezados style={{ position: 'sticky', top: 0 }}> a las etiquetas th

Hola @JCofman , probé tu corrección de CSS pero no me funciona. Aquí está el ejemplo de la zona de pruebas de código: https://codesandbox.io/s/react-window-with-table-elements-forked-huti6?file=/src/index.tsx : 514-542

Hay dos problemas:

  1. El encabezado no se pega en la parte superior a pesar de tener estilos en su lugar.
  2. El encabezado parpadea / parpadea mientras se desplaza mucho hacia arriba.
    [Pasos para reproducir: desplácese 50% hacia abajo, luego desplácese rápidamente hacia arriba y observe el movimiento del encabezado].

Gracias por adelantado :)

@jamesmfriedman Me encanta el enfoque. Idea realmente interesante para usar Context para lograr esto. Me gusta el hecho de que solo renderiza una tabla, a diferencia de mi enfoque donde renderizo dos, una para el encabezado y otra para el cuerpo. Esto ha causado mucha confusión (no lo vi venir tbh) en los usuarios de la tabla de ventana.

Intentaré usar estas ideas en la mesa de la ventana si no le importa. Creo que el mayor desafío sería descubrir cómo ajustar automáticamente el tamaño de las filas en función del contenido (que fue al menos el mayor desafío de la tabla de ventanas).

Terminé encontrando una solución muy similar porque estoy usando display: grid con display: contents para las filas, así que básicamente se adhiere a la restricción de los elementos table > tr .

Usó context + refs para llamar a un método interno, sin embargo, esto es totalmente innecesario ya que se puede lograr lo mismo buscando el valor style.top del primer hijo:

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

La biblioteca proporciona el estilo prop cuando realiza createElement para cada fila, y dado que su componente Row no asigna el style a tr , usted no pasándolos tampoco.

Recientemente, me preguntaba algo como esto. ¿Qué pasaría si moviéramos todo el cuerpo de la tabla con relleno usando pseudo elementos (después / antes) a través de las propiedades personalizadas de css?

En ejemplo, usando react-virtual sea https://codesandbox.io/s/poc-react-virtual-table-jyz0m

Necesitaba absolutamente una tabla para poder lograr la UX que quería y no estaba satisfecho con las soluciones para hacerlo funcionar con react-window .

Terminé extrayendo la lógica de virutalización yo mismo en un gancho e implementándola con dos tablas, una para los encabezados y la segunda para el contenido, similar a lo que se publicó anteriormente. La diferencia en mi enfoque es que la segunda tabla todavía tenía sus encabezados en el marcado pero visualmente ocultos para que permaneciera accesible, mientras que los encabezados de la primera estaban allí solo para usuarios videntes y posicionalmente pegados. Funcionó como un encanto, y el código era drásticamente más simple.

Simplemente tirándolo en caso de que alguien quiera intentarlo.

@piecyk Eso es genial, muy similar a cómo se implementa virtuoso: https://virtuoso.dev/grouped-by-first-letter/

Debo agregar que posicionar todo el contenedor con absoluta / superior funciona correctamente, pero al ejecutar una auditoría de rendimiento con Chrome, veo problemas de CLS que podrían afectar a FPS. No creo que el impacto sea notable, puedes ver lo rápido y suave que puede ser en este ejemplo: https://b6udg.csb.app/ (aunque es un contenedor de gran tamaño). Sin embargo, con padding-top / bottom no hay CLS!

Intentaré esto yo mismo y mediré la diferencia pronto.

@pupudu Aplaudo sus esfuerzos de código abierto y

Resulta que pude lograr esto con un poco de código y mucha creatividad. El código es lo suficientemente simple como para que cualquiera pueda copiar / pegar y extender a su gusto. @bvaughn después de averiguar cómo, sería bastante trivial agregar esto a la biblioteca si lo desea, pero también bastante trivial de anular si sabe cómo.

_En resumen_:

  • Capture el estilo "superior" de la primera fila después de un renderizado
  • Almacene ese valor en React.Context para que podamos pasarlo
  • Aplicar el valor a un componente de la tabla que será lo que se mueva, en lugar de las filas en sí
  • Agregue espacios adicionales para cosas como encabezados y pies de página.
  • La ergonomía de la biblioteca no permite que las cosas se pasen ordenadamente, por lo que React.Context es el héroe para superar la comunicación entre componentes.

Sandbox de código de trabajo: https://codesandbox.io/s/react-window-with-table-elements-d861o

Código de referencia

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

Gracias por esto, ¿alguien ha intentado (con éxito) usar esto con el componente InfiniteLoader ? ... He intentado patearlo pero tengo problemas con el "envoltorio" interno, que Supongo que se debe a que el cargador infinito está pasando accesorios inesperados al innerElementType.

¿Fue útil esta página
0 / 5 - 0 calificaciones