React-window: Usando elemento de tabela html

Criado em 21 set. 2018  ·  34Comentários  ·  Fonte: bvaughn/react-window

Você tem um exemplo de como usar isso com tabelas? Estou tentando fazer funcionar, mas tenho uma forte suspeita de que não funciona ou que estou fazendo algo muito errado.
Eu configurei outerTagName para "table" e innerTagName para "tbody", mas não recebo nenhuma rolagem.

Aqui está meu código, não tenho certeza se ajuda (itens é uma 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

Comentários muito úteis

@pupudu Eu aplaudo seus esforços de código aberto e examinei seu código exaustivamente antes de decidir tentar resolver isso sozinho. Eu só queria a coisa super simples que outras pessoas estavam procurando: esta biblioteca, mas com elementos de tabela.

Acontece que fui capaz de fazer isso com um código muito pequeno e muita criatividade. O código é simples o suficiente para que qualquer pessoa possa copiar / colar e estender a seu gosto. @bvaughn depois de descobrir como, seria bastante trivial adicionar à biblioteca se você quiser, mas também muito trivial substituir se você souber como.

Resumindo :

  • Capture o estilo "superior" da primeira linha após uma renderização
  • Armazene esse valor em React.Context para que possamos distribuí-lo
  • Aplique o valor a um componente da tabela que será o que será movido, ao invés das próprias linhas
  • Adicione slots adicionais para itens como cabeçalhos e rodapés.
  • A ergonomia da biblioteca não permite que as coisas sejam transmitidas de maneira organizada, então o React.Context é o herói para superar a comunicação entre componentes.

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

Código para referência

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 comentários

Eu não acho que isso realmente funcionará. Pelo que sei, HTMLTableElement realmente não suporta estouro da maneira que um componente de janelamento precisaria. Você _poderia_ mudar o estilo para display: block mas ainda não acho que funcionaria direito. ( Aqui está um exemplo .)

Por que você precisa de uma mesa? Acho que você pode obter o mesmo layout de coluna com blocos embutidos ou layouts flexíveis.

Algumas coisas adicionais:

  • Você não está especificando um parâmetro obrigatório width para List
  • Você não precisa adicionar key às suas linhas
  • itemSize pode ser apenas 30 vez de () => 30 (terá um desempenho ligeiramente melhor)

Obrigado pela resposta rápida.
Já discutimos como reescrevê-lo para divs, mas como se trata de um componente bastante grande existente, só queríamos ter certeza antes de começar a reescrever. Eu vi alguns outros comentários sobre rc-table, então pensei que talvez devesse funcionar.
De qualquer forma, não se preocupe em reescrever, acho que valerá a pena.

Recebi avisos do React sobre chaves sem a chave na linha, o que também foi confuso para mim, considerando que estou definindo um itemKey, então não tenho certeza do que está acontecendo lá.

Obrigado

Einar

Em 21 de setembro de 2018, às 16:52, Brian Vaughn [email protected] escreveu:

Eu não acho que isso realmente funcionará. Até onde sei, HTMLTableElement realmente não oferece suporte a estouro da maneira que um componente de janela precisaria. Você poderia alterar o estilo de display: block, mas ainda não acho que funcionaria direito.

Por que você precisa de uma mesa? Acho que você pode obter o mesmo layout de coluna com blocos embutidos ou layouts flexíveis.

Algumas coisas adicionais:

Você não está especificando uma largura de parâmetro necessária para Listar
Você não precisa adicionar a chave às suas linhas
itemSize pode ser apenas 30 em vez de () => 30 (terá um desempenho ligeiramente melhor)
-
Você está recebendo isto porque é o autor do tópico.
Responda a este e-mail diretamente, visualize-o no GitHub ou ignore a conversa.

Recebi avisos do React sobre chaves sem a chave na linha, o que também foi confuso para mim, considerando que estou definindo um itemKey, então não tenho certeza do que está acontecendo lá.

Isso não é esperado. Você pode compartilhar um caso de reprodução comigo (via Code Sandbox)?

Ooh, desculpe. Devo acrescentar que isso _somente_ seria esperado se sua função itemKey retornasse undefined para alguns itens - nesse caso, a correção precisaria ser para essa função.

@bvaughn Não estou achando nada específico sobre o suporte de estouro ser um fator limitante. Curiosamente, gostaria de saber o que pode ser. Eu certamente entendo que o suporte à funcionalidade principal para ambos pode ser entediante, mas parece apenas que o elemento da lista base é tudo o que está fora da escolha do implementador no momento. Falando por mim e por qualquer pessoa que possa estar usando a janela de reação com cenários de cabeçalho fixo, seria bom obter o suporte tabular inerente embutido nos layouts de tabela. O uso de display: table consome "alguma" sobrecarga de desenvolvimento e é um layout único para aplicativos adjacentes que podem ser construídos acidentalmente usando tabelas. Eu sempre poderia ajustar o estouro ou outras propriedades via css (estou fazendo isso em uma implementação atual de qualquer maneira). Um acessório elementType está fora de questão?

@bvaughn Não estou achando nada específico sobre o suporte de estouro ser um fator limitante.

Talvez eu não tenha usado o melhor fraseado - mas se você olhar o Sandbox ao qual criei um rrn61wkzwm ), verá o comportamento inesperado.

Aqui está outro exemplo que é apenas HTML: lx65871p69

O HTMLSelectElement (o <tbody> ) ignora estilos como height e overflow - tornando-o inútil para janelas. Você pode "consertar" isso alterando seu modo de exibição ( display: block ), mas isso anula o propósito de usar um HTMLTableElement porque quebra o comportamento de dimensionamento da coluna.

Esse comportamento de dimensionamento de coluna é quebrado para começar no caso de uso de janelas de qualquer maneira, porque o tamanho depende do conteúdo nas colunas (que mudaria conforme o usuário rolasse).

mas parece apenas que o elemento da lista base é tudo o que está fora da escolha do implementador no momento ... Um elemento elementType está fora de questão?

Você já pode especificar o tipo de tag se realmente quiser ( 2j0z718mwy ), embora eu não veja o que você ganha.

Falando por mim e por qualquer pessoa que possa estar usando a janela de reação com cenários de cabeçalho fixo, seria bom obter o suporte tabular inerente embutido nos layouts de tabela.

Usar um HTMLTableElement seria problemático pelos motivos que expliquei acima. Se quiser um layout tabular / de grade com um cabeçalho fixo, você pode fazer isso usando um dos componentes regulares da lista: pk78pvwnkx

Por que você precisa de uma mesa? Acho que você pode obter o mesmo layout de coluna com blocos embutidos ou layouts flexíveis.

Talvez uma estrutura de tabela seja necessária para alguma acessibilidade (como, melhor leitura de tela em comparação com divs não semânticos)? Estou supondo aqui.

Nesse caso, eu substituiria o estilo de exibição CSS e quaisquer outros estilos inerentes aos elementos da tabela.

Minha experiência na construção de um componente de tabela complexo (com cabeçalhos fixos / fixados de cima e à esquerda, estrutura colspan / rowspan complicada e um modelo de dados para descrever a estrutura por trás dele) cerca de nove anos atrás (então não tínhamos React, nenhuma preocupação de acessibilidade, e, pelo que me lembro, nenhuma virtualização) é que meu colega responsável por esse componente tentou implementá-lo em elementos de tabela adequados, lutou para forçar o layout a se comportar de certas maneiras e, então, decidiu implementar o layout da tabela em JavaScript puro de divs absolutamente posicionados e dimensionados. Essa solução funcionou perfeitamente: sem saltos estranhos de layout, rolagem suave (não por linha / coluna), etc.

Talvez uma estrutura de tabela seja necessária para alguma acessibilidade (como, melhor leitura de tela em comparação com divs não semânticos)? Estou supondo aqui.

Funções de Aria podem ser usadas para este 😄

Super completo, obrigado. Você acabou de me salvar de ir até a toca do coelho para tentar um protótipo que resolveria um problema apenas para criar outros.

Oi
Tentei encontrar um thread existente em vez de criar um novo. Espero estar no bom lugar ...

Existe uma maneira de ter uma espécie de "colspan" para mesclar duas células para FixedSizeGrid (provavelmente faria mais sentido no meu contexto)?
Essa questão depende de outra discussão iniciada aqui , mas para fazer mais sentido prefiro postar aqui.

Existe uma maneira de ter uma espécie de "colspan" para mesclar duas células para FixedSizeGrid (provavelmente faria mais sentido no meu contexto)?

Não. Isso não é compatível.

@einarq @BlaineBradbury @sompylasar Caso você ainda esteja interessado, consegui um trabalho de janelamento com tags de tabela. Na verdade, acabei criando uma janela de reação de agrupamento de biblioteca. Você pode ver uma demonstração ao vivo aqui: https://window-table.netlify.com/

A razão pela qual eu queria as tags de mesa era principalmente para estilizar. Definir o estilo de uma tabela html regular é muito fácil com tw-bootstrap e, portanto, eu queria fazer o mesmo com uma mesa com janela.

Estou tentado a marcar e perguntar a Brian o que ele pensa sobre essa implementação (já que ele mencionou que as tags de tabela html não funcionariam). Mas ele já me ajudou o suficiente. Então, vou deixar isso para vocês.

Obrigada.

Essa é uma abordagem interessante @pupudu. Não sei se haveria alguma preocupação com acessibilidade por ter tags <table> separadas para os cabeçalhos e linhas, mas uma boa ideia caso contrário.

Estou tentando usar react-datasheet com react-window , basicamente 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>,
      };
    }),
  );
}

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

Ei, estou tentando fazer isso com React-Table e percebendo que preciso usar divs em vez de elementos de tabela. O problema com isso é que as bibliotecas de estilo, como material / bootstrap, dependem desses elementos. Portanto, é muito mais trabalhoso alterar meu esquema de tabela para divs em vez de elementos de tabela. É possível usar elementos de tabela para diffs ou eu tenho que criar meu próprio estilo agora?

@simkessy A menos que você já tenha notado, você deve verificar a mesa da janela. Ele permite o uso de tags de tabela html, permitindo a reutilização de estilos existentes. Tenho que admitir que a mesa não é tão avançada quanto a mesa reativa.
https://window-table.netlify.com/

@pupudu Eu aplaudo seus esforços de código aberto e examinei seu código exaustivamente antes de decidir tentar resolver isso sozinho. Eu só queria a coisa super simples que outras pessoas estavam procurando: esta biblioteca, mas com elementos de tabela.

Acontece que fui capaz de fazer isso com um código muito pequeno e muita criatividade. O código é simples o suficiente para que qualquer pessoa possa copiar / colar e estender a seu gosto. @bvaughn depois de descobrir como, seria bastante trivial adicionar à biblioteca se você quiser, mas também muito trivial substituir se você souber como.

Resumindo :

  • Capture o estilo "superior" da primeira linha após uma renderização
  • Armazene esse valor em React.Context para que possamos distribuí-lo
  • Aplique o valor a um componente da tabela que será o que será movido, ao invés das próprias linhas
  • Adicione slots adicionais para itens como cabeçalhos e rodapés.
  • A ergonomia da biblioteca não permite que as coisas sejam transmitidas de maneira organizada, então o React.Context é o herói para superar a comunicação entre componentes.

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

Código para referência

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 Adoro a abordagem. Ideia realmente interessante para usar Context para conseguir isso. Gosto do fato de você renderizar apenas uma tabela, ao contrário da minha abordagem em que renderizo duas, uma para o cabeçalho e outra para o corpo. Isso causou muita confusão (não vi isso chegando) em usuários de mesa de janela.

Tentarei usar essas idéias na mesa da janela, se você não se importar. Acho que o maior desafio seria descobrir como dimensionar automaticamente as linhas com base no conteúdo (que foi o maior desafio do windo-table, pelo menos).

@jamesmfriedman que parece muito promissor. @bvaughn , você tem alguma preocupação com essa abordagem?

@jamesmfriedman parece que o cabeçalho rola para cima junto com o conteúdo. É possível torná-lo pegajoso?

É de fato possível, e não específico para esta implementação. Basta pesquisar alguns cabeçalhos fixos no Google com elementos de tabela HTML. Lembro-me vagamente de que a maneira de fazer isso é, na verdade, tornar a posição do elemento TH fixa.

@jamesmfriedman muito obrigado pelo seu exemplo 👍 ajudou muito @marink aqui é um exemplo para cabeçalhos https://codesandbox.io/s/react-window-with-table-elements-jj70e você precisa adicionar style={{ position: 'sticky', top: 0 }}> para as tags th

@jamesmfriedman Muito obrigado pelo exemplo. Vou tentar usá-lo. Mas acho que usar top: 0 e position sticky em vez de recalcular top e ter a posição absoluta no elemento da tabela seria o caminho a percorrer. Porque se você está usando a posição sticky para obter cabeçalhos fixos nos elementos th, então tudo fica fodido na rolagem rápida.

Portanto, a linha 79 seria <table style={{ top: 0, position: 'sticky', width: '100%' }}>

Isso também corrige o problema de rolagem que ocorre se você continuar rolando quando estiver na parte inferior.

Outro bom motivo para usar tabelas HTML é o suporte ao IE11. Estou usando FixedSizeList e, em seguida, estilizando-o com CSS-grid. No entanto, quando você precisa adicionar outros recursos como colunas responsivas, colunas com tamanhos diferentes, etc, e fazer tudo funcionar junto com o IE11, você acaba com uma grande quantidade de código. Enquanto as tabelas HTML funcionam por padrão sem código.

IE11 não é algo que você deveria apoiar mais :)

Cada desenvolvedor FE quer a morte do IE11. Mas, infelizmente, existem empresas / pessoas presas a isso, incapazes de fazer a mudança por motivos que nem sequer podemos imaginar. Felizmente, ainda faltam alguns anos no tempo. Até isso, precisaremos oferecer suporte ao IE11, gostemos ou não.

Foi uma pílula difícil de engolir para mim também.

@ olafur164 obrigado por sua correção para cabeçalhos

Deixe-me compartilhar minha experiência com o react-virtualized (até mesmo sua página de problemas com a janela react) e alguma biblioteca de IU de material chamada https://github.com/mbrn/ material-table / issues / 891 # issuecomment -681986647. Ainda existem alguns problemas, como problemas de estilo e aviso validateDOMNesting(...) .

@jamesmfriedman muito obrigado pelo seu exemplo 👍 ajudou muito @marink aqui é um exemplo de cabeçalhos style={{ position: 'sticky', top: 0 }}> às tags th

Olá @JCofman , tentei corrigir seu CSS, mas não está funcionando para mim. Aqui está o exemplo de sandbox de código - https://codesandbox.io/s/react-window-with-table-elements-forked-huti6?file=/src/index.tsx : 514-542

Existem dois problemas -

  1. o cabeçalho não está grudando no topo, apesar de ter estilos no lugar.
  2. o cabeçalho está piscando / piscando enquanto rola fortemente na direção para cima.
    [Passos para reproduzir - role 50% para baixo e, em seguida, role rapidamente para cima e observe o movimento do cabeçalho].

Desde já, obrigado :)

@jamesmfriedman Adoro a abordagem. Ideia realmente interessante para usar Context para conseguir isso. Gosto do fato de você renderizar apenas uma tabela, ao contrário da minha abordagem em que renderizo duas, uma para o cabeçalho e outra para o corpo. Isso causou muita confusão (não vi isso chegando) em usuários de mesa de janela.

Tentarei usar essas idéias na mesa da janela, se você não se importar. Acho que o maior desafio seria descobrir como dimensionar automaticamente as linhas com base no conteúdo (que foi o maior desafio do windo-table, pelo menos).

Acabei encontrando uma solução muito semelhante porque estou usando display: grid com display: contents para as linhas, então basicamente adere à restrição de table > tr elementos.

Você usou context + refs para chamar um método interno, no entanto, isso é totalmente desnecessário, pois o mesmo pode ser feito procurando o valor style.top do primeiro filho:

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

O estilo prop é fornecido pela biblioteca quando ela executa createElement para cada linha, e uma vez que seu componente Row particular não atribui style a tr , você não passando-os para baixo também.

Recentemente, estava me perguntando sobre algo assim. E se movêssemos todo o corpo da tabela com preenchimento usando pseudoelementos (depois / antes) por meio de propriedades personalizadas css?

Por exemplo, usando react-virtual be https://codesandbox.io/s/poc-react-virtual-table-jyz0m

Eu precisava absolutamente de uma mesa para conseguir a experiência do usuário que queria e não estava satisfeito com as soluções alternativas para fazê-la funcionar com react-window .

Acabei extraindo sozinho a lógica de virutalização para um gancho e implementando com duas tabelas, uma para os cabeçalhos e outra para o conteúdo, semelhante ao que foi postado acima. A diferença na minha abordagem é que a segunda tabela ainda tinha seus cabeçalhos na marcação, mas visualmente ocultos para que permanecesse acessível, enquanto os cabeçalhos da primeira estavam lá apenas para usuários com visão e fixados na posição. Funcionou como um encanto, e o código era drasticamente mais simples.

Apenas jogando lá fora, no caso de alguém querer tentar isso.

@piecyk Esse é ótimo, muito semelhante a como o virtuoso é implementado: https://virtuoso.dev/grouped-by-first-letter/

Devo acrescentar que o posicionamento de todo o contêiner com absoluto / superior apresenta desempenho, mas, ao executar uma auditoria de desempenho com o Chrome, vejo problemas de CLS que podem impactar o FPS. Não acho que o impacto seja perceptível, você pode ver o quão rápido e suave pode ser neste exemplo: https://b6udg.csb.app/ (embora seja um contêiner de grande porte). No entanto, com padding-top / bottom não há CLS!

Vou tentar fazer isso sozinho e medir a diferença em breve.

@pupudu Eu aplaudo seus esforços de código aberto e examinei seu código exaustivamente antes de decidir tentar resolver isso sozinho. Eu só queria a coisa super simples que outras pessoas estavam procurando: esta biblioteca, mas com elementos de tabela.

Acontece que fui capaz de fazer isso com um código muito pequeno e muita criatividade. O código é simples o suficiente para que qualquer pessoa possa copiar / colar e estender a seu gosto. @bvaughn depois de descobrir como, seria bastante trivial adicionar à biblioteca se você quiser, mas também muito trivial substituir se você souber como.

_ Resumindo_:

  • Capture o estilo "superior" da primeira linha após uma renderização
  • Armazene esse valor em React.Context para que possamos distribuí-lo
  • Aplique o valor a um componente da tabela que será o que será movido, ao invés das próprias linhas
  • Adicione slots adicionais para itens como cabeçalhos e rodapés.
  • A ergonomia da biblioteca não permite que as coisas sejam transmitidas de maneira organizada, então o React.Context é o herói para superar a comunicação entre componentes.

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

Código para referência

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

Obrigado por isso, alguém tentou (com sucesso) usar isso com o componente InfiniteLoader ? ... Eu tentei chutá-lo, mas estou tendo problemas com o "invólucro" interno, que suponha que seja porque o carregador infinito está passando propriedades inesperadas para o innerElementType.

Esta página foi útil?
0 / 5 - 0 avaliações