React-window: Compatibilidad con ScrollSync

Creado en 8 nov. 2018  ·  52Comentarios  ·  Fuente: bvaughn/react-window

Tengo un caso de uso en el que necesito crear una cuadrícula que tenga encabezados fijos que permanezcan anclados en la parte superior. En react-virtualized, esto podría lograrse creando dos componentes de cuadrícula (uno para los encabezados, otro para el cuerpo de la cuadrícula) y sincronizando la posición de desplazamiento para que el desplazamiento horizontal de la cuadrícula del encabezado se sincronice con la cuadrícula principal.

Sé que está tratando de mantener una ventana de reacción más liviana, tanto en términos de tamaño de paquete como de complejidad conceptual, a favor de construir funcionalidades adicionales como paquetes separados. Esa es una dirección con la que estoy de acuerdo. Creo que el componente ScrollSync de react-virtualized podría extraerse o adaptarse para trabajar con react-window. Sin embargo, para hacerlo, react-window necesitaría aceptar accesorios para scrollLeft y scrollTop para que el desplazamiento de desplazamiento de la cuadrícula del encabezado pueda administrarse directamente.

¿Es este un caso de uso que estaría dispuesto a apoyar? Si no es así, ¿tiene algún consejo sobre en qué dirección debería ir para implementar esto yo mismo?

Gracias por trabajar en esta biblioteca. Como alguien que ha usado react-virtualized durante un par de años, aprecio la simplicidad de comenzar con react-window y el rendimiento que ha tenido para mí sin mucha intervención manual.

Comentario más útil

Eso tiene mucho sentido. ¡Gracias por la sugerencia! Lo hice funcionar, y en realidad es bastante fácil de configurar. Tan fácil que definitivamente no garantiza un paquete independiente. Tal vez un ejemplo en los documentos, pero supongo que esa es tu decisión. Dejaré un enlace a mi ejemplo de Code Sandbox en funcionamiento aquí en caso de que alguien más se encuentre con este problema en el futuro.

https://codesandbox.io/s/y3pyp85zm1

TLDR: coloque una referencia en la cuadrícula del encabezado y colóquela en la cuadrícula del cuerpo.

onScroll={({ scrollLeft }) => this.headerGrid.current.scrollTo({ scrollLeft })}

Todos 52 comentarios

En primer lugar, gracias por las amables palabras y los comentarios positivos. ¡Me alegra saber que react-window le ha funcionado bien hasta ahora!

Estoy de acuerdo en que un componente como ScrollSync podría lanzarse como un paquete independiente que depende de react-window, pero no quisiera agregarlo a este proyecto ya que no es fundamental para las ventanas.

Con respecto a su pregunta específica sobre los accesorios de desplazamiento, este no es un cambio que estaría dispuesto a hacer en el proyecto porque después de usarlo con react-virtualized, me di cuenta de que tiene algunos inconvenientes serios. De hecho, escribí sobre esto en el blog de React si tienes curiosidad:

https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#anti -pattern-erasing-state-when-props-change

Puede usar la API de desplazamiento imperativa que ofrece react-window para lograr un comportamiento de sincronización similar. Solo tendría que llamar a estos métodos desde los ciclos de vida de confirmación en lugar de pasar accesorios.

Con suerte, esto tiene sentido, pero si no, no dude en hacer preguntas de seguimiento.

Eso tiene mucho sentido. ¡Gracias por la sugerencia! Lo hice funcionar, y en realidad es bastante fácil de configurar. Tan fácil que definitivamente no garantiza un paquete independiente. Tal vez un ejemplo en los documentos, pero supongo que esa es tu decisión. Dejaré un enlace a mi ejemplo de Code Sandbox en funcionamiento aquí en caso de que alguien más se encuentre con este problema en el futuro.

https://codesandbox.io/s/y3pyp85zm1

TLDR: coloque una referencia en la cuadrícula del encabezado y colóquela en la cuadrícula del cuerpo.

onScroll={({ scrollLeft }) => this.headerGrid.current.scrollTo({ scrollLeft })}

Gracias por el enlace! Eso es muy reflexivo.

El jueves 8 de noviembre de 2018 a la 1:09 p.m., Reagan Keeler < [email protected] escribió:

Eso tiene mucho sentido. ¡Gracias por la sugerencia! Lo tengo funcionando
y en realidad es bastante fácil de configurar. Tan fácil que definitivamente no lo hace
garantizan un paquete independiente. Tal vez un ejemplo en los documentos, pero ese es tu
llamar supongo. Dejaré un enlace a mi ejemplo de Code Sandbox de trabajo aquí
en caso de que alguien más se tope con este problema en el futuro.

https://codesandbox.io/s/y3pyp85zm1

TLDR: coloque una referencia en la cuadrícula del encabezado y colóquela en la cuadrícula del cuerpo.

onScroll = {({scrollLeft}) => this.headerGrid.current.scrollTo ({scrollLeft})}

-
Recibe esto porque modificó el estado abierto / cerrado.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/bvaughn/react-window/issues/86#issuecomment-437156749 ,
o silenciar el hilo
https://github.com/notifications/unsubscribe-auth/AABznTUunzEIs6bVQfVsz7T21L2-Pkkoks5utJ17gaJpZM4YVMd7
.

Para cualquiera que se haya encontrado con esto, tenía una superrejilla congelada superior / derecha / inferior / izquierda sincronizada con desplazamiento en react-virtualized que acabo de migrar a esta biblioteca. Diré que la solución anterior es al menos igual de eficaz, si no mejor, y sustancialmente menos dolorosa que usar ScrollSync en la otra biblioteca.

También se empareja bastante bien con el nuevo gancho de reacción useRef (https://reactjs.org/docs/hooks-reference.html#useref)

const topRef = useRef();
const rightRef = useRef();
const bottomRef = useRef();
const leftRef = useRef();
...
<Grid
  onScroll={({ scrollLeft, scrollTop }) => {
    if (leftRef.current) {
      leftRef.current.scrollTo({ scrollTop });
    }
    if (rightRef.current) {
      rightRef.current.scrollTo({ scrollTop });
    }
    if (topRef.current) {
      topRef.current.scrollTo({ scrollLeft });
    }
    if (bottomRef.current) {
      bottomRef.current.scrollTo({ scrollLeft });
    }
  }}
  ...
/>

¡Bonito! ¡Gracias por compartir @ranneyd!

También se empareja bastante bien con el nuevo useRef react hook (https://reactjs.org/docs/hooks-reference.html#useref)

Gracias @ranneyd

¿Sería posible compartir un ejemplo práctico?

@ranneyd gracias de nuevo, seguí tu enfoque y la sincronización de desplazamiento funciona bien.

El desbordamiento oculto en las barras de desplazamiento provoca una desalineación cerca del final de la cuadrícula:

gridalignement

Alguna sugerencia en como arreglar esto?

Ejemplo de CodeSandbox

Gracias por adelantado

@carlosagsmendes es por la barra de desplazamiento. En el de la izquierda, haga que la altura sea la altura menos el tamaño de la barra de desplazamiento. Para obtener coherencia en todos los dispositivos, codifico manualmente el tamaño de la barra de desplazamiento con CSS. Si su contenido es tan dinámico que puede haber o no una barra de desplazamiento, haga algo como "num elementos * tamaño elementos <ancho (debe tener todos esos valores allí mismo)" y luego cambie la altura dependiendo de eso.

Para obtener coherencia en todos los dispositivos, codifico manualmente el tamaño de la barra de desplazamiento con CSS.

FWIW, el paquete dom-helpers tiene una práctica función scrollbarSize que le dice cuál es el ancho en el dispositivo actual. Podría ser mejor que la codificación fija.

@bvaughn ¡sí! Me olvidé de mencionar eso. Sin embargo, no funcionó para nosotros. Creo que el problema era que ya estábamos haciendo barras de desplazamiento codificadas y eso lo confundía

Gracias. ¡Voy a darle una oportunidad!

¿Funciona esto con VariableSizeList horizontal? Lo intenté pero mi navegador se congela.

onScroll={({ scrollLeft }) => this.headerGrid.current.scrollTo({ scrollLeft })}

Funciona bien con VariableSizeGrid pero mi encabezado se retrasa un poco en el desplazamiento.

@ajaymore también lo es el retraso del encabezado: la sincronización de desplazamiento tiene problemas cuando usa la rueda de desplazamiento en una Mac. MacOSX tiene esta función de desplazamiento natural incorporada en la que interpola el desplazamiento para hacerlo "más suave". La frecuencia de actualización de esto es en realidad más rápida que la frecuencia de cuadros de animación en Chrome. Por lo tanto, el elemento que está desplazando se animará antes de que V8 / ScrollSync tenga tiempo de actualizar el DOM. Esta es una limitación técnica para la que no he visto una solución.

Dato curioso: si usa manualmente la barra de desplazamiento en el navegador (como desplazarse a la antigua usanza arrastrando la pequeña cosa), funciona totalmente bien. La interpolación está integrada en la rueda de desplazamiento / deslizamiento del panel de seguimiento

@ajaymore también lo es el retraso del encabezado: la sincronización de desplazamiento tiene problemas cuando usa la rueda de desplazamiento en una Mac. MacOSX tiene esta función de desplazamiento natural incorporada en la que interpola el desplazamiento para hacerlo "más suave". La frecuencia de actualización de esto es en realidad más rápida que la frecuencia de cuadros de animación en Chrome. Por lo tanto, el elemento que está desplazando se animará antes de que V8 / ScrollSync tenga tiempo de actualizar el DOM. Esta es una limitación técnica para la que no he visto una solución.

Dato curioso: si usa manualmente la barra de desplazamiento en el navegador (como desplazarse a la antigua usanza arrastrando la pequeña cosa), funciona totalmente bien. La interpolación está integrada en la rueda de desplazamiento / deslizamiento del panel de seguimiento

@ranneyd Gracias por una respuesta tan rápida. Estoy de acuerdo que es una limitación. Funcionará bien en la mayoría de los dispositivos, por lo que no es una gran preocupación.

@bvaughn, ¿alguna vez has jugado con position: sticky ? No lo he visto en un tiempo y sé que el soporte del navegador es escaso, pero me pregunto si hay una manera de aprovecharlo en los navegadores donde es compatible ...

Tengo el mismo problema que @ajaymore , es decir, la sincronización de dos componentes es lenta (como se indicó, funciona bien en el desplazamiento manual). Estoy probando con Chrome en una PC, así que aparentemente ese no es un problema exclusivo de Mac ... ¿Alguien ha tenido éxito resolviendo este problema de alguna manera?

@alonrbar Tengo el mismo problema en una PC con Windows.

De hecho, acabo de encontrar una solución que me funciona.
La idea general es crear una "cuadrícula de sombra" que se esconda en la cuadrícula original y robe su evento onScroll y luego lo use para desplazarse manualmente por la cuadrícula original, así como cualquier otra cuadrícula necesaria. Ralentiza un poco el rendimiento, pero mantiene todas las cuadrículas bien sincronizadas, por lo que tendrá que considerar la compensación.

El código tiene este aspecto;

import styled from '@emotion/styled';
import * as React from 'react';
import { VariableSizeGrid, VariableSizeGridProps } from 'react-window';
import { SizeUtils } from '../utils';

export interface SyncableGridProps extends VariableSizeGridProps {
    mainGridRef: React.Ref<VariableSizeGrid>;
    shadowGridRef: React.Ref<VariableSizeGrid>;
    hideVerticalScrollbar?: boolean;
}

export class SyncableGrid extends React.PureComponent<SyncableGridProps> {
    public render() {
        const { height, width } = this.props;
        const {
            onScroll,
            mainGridRef: mainGridRef1,
            shadowGridRef: shadowGridRef1,
            ...mainProps
        } = this.props;
        const {
            children,
            style,
            overscanRowsCount,
            overscanColumnsCount,
            overscanCount,
            useIsScrolling,
            onItemsRendered,
            mainGridRef: mainGridRef2,
            shadowGridRef: shadowGridRef2,
            innerRef,
            outerRef,
            ...shadowProps
        } = this.props;
        return (
            <SyncWrapper
                style={{
                    height,
                    width
                }}
            >
                <MainGrid
                    {...mainProps}
                    style={Object.assign({}, style, {
                        overflowY: 'scroll'
                    })}
                    ref={mainGridRef1}
                />
                <GridShadow
                    {...shadowProps}
                    style={{
                        position: 'absolute',
                        top: 0,
                        left: 0
                    }}
                    ref={shadowGridRef1}
                >
                    {() => null}
                </GridShadow>
            </SyncWrapper>
        );
    }
}

// ---------------- //
//      styles      //
// ---------------- //

const SyncWrapper = styled.div`
    position: relative;
    overflow: hidden;
`;

export interface MainGridProps extends VariableSizeGridProps {
    hideVerticalScrollbar?: boolean;
}

export const MainGrid = styled(VariableSizeGrid) <MainGridProps>`
    overflow-y: scroll;
    box-sizing: content-box;
    ${props => {
        if (!props.hideVerticalScrollbar)
            return '';
        const paddingDir = (props.theme.dir === 'rtl' ? 'padding-left' : 'padding-right');
        return `${paddingDir}: ${SizeUtils.scrollbarWidth}px;`;
    }}
`;

export const GridShadow = styled(MainGrid)`
    opacity: 0;
`;

Luego en otro archivo:

<SyncableGrid
    mainGridRef={this.firstGridMain}
    shadowGridRef={this.firstGridShadow}
    onScroll={this.handleFirstGridScroll}
    // other props omitted for bravity...
>
   // children omitted for bravity...
</SyncableGrid>
<SyncableGrid
    mainGridRef={this.secondGridMain}
    shadowGridRef={this.secondGridShadow}
    onScroll={this.handleSecondGridScroll}
    // other props omitted for bravity...
>
   // children omitted for bravity...
</SyncableGrid>

private handleFirstGridScroll = (e: GridOnScrollProps) => {
    const { scrollTop, scrollLeft } = e;

    // synchronize self
    if (this.firstGridMain.current) {
        this.firstGridMain.current.scrollTo({ scrollTop, scrollLeft });
    }

    // synchronize other grid
    if (this.secondGridMain.current) {
        this.secondGridMain.current.scrollTo({ scrollTop, scrollLeft });
        this.secondGridShadow.current.scrollTo({ scrollTop, scrollLeft });
    }
}

@alonrbar ¡ esto es fascinante! Probaré esto pronto.

Parece que la cuadrícula de sombras vive por encima de la cuadrícula real, ¿no? Si es así, haga clic en los eventos en la "cuadrícula real" no funcionará, ¿verdad? Supongo que podría hacer algo un poco complicado con eventos de clic + coords x / y y de alguna manera aplicar eso a la cuadrícula principal.

También @barbalex re: falla en Windows también

En realidad, podría ser una bandera cromada. Vea si hay algo en chrome: // flags. Es posible que Microsoft también haya implementado algo similar.

Ciertamente, el problema parece estar relacionado con el hecho de que el desplazamiento se "suaviza" más rápido que el marco de animación del navegador. Si le preocupa que sea puramente una cuestión de rendimiento / retraso, intente hacer una implementación de sincronización de desplazamiento muy básica (donde el desplazamiento de un div establece la posición de desplazamiento de otro) y vea si tiene el mismo problema. Tal vez incluso hacerlo en JS puro de vainilla para eliminar a React como el culpable

Ahhh @ranneyd tienes razón, bloquea los eventos de clic ... Necesito pensar un poco más en eso ...

Creo que la función de desplazamiento por hilos en chrome: // flags tiene una gran influencia: desactivarla hace que el desplazamiento con la rueda del mouse sea tan suave como el desplazamiento con la barra de desplazamiento para mí.

2019-07-15_00h03_42

@alonrbar ¿qué pasa con esto?

Agregue un controlador de desplazamiento a la cuadrícula principal. En él haces e.preventDefault(); o algo para evitar el desplazamiento real. Luego miras el evento para averiguar cuánto SE HABRÍA desplazado, lo que hace para mover las otras cosas sincronizadas, pero luego lo usas para desplazar manualmente ese mismo elemento. Entonces, en lugar de desplazar A, luego usar esa información para desplazar B, intercepta el desplazamiento en A, lo cancela y luego lo usa para desplazar B y A en sí. Funcionaría eso?

No estoy junto a una computadora para probar. Bastante hacky pero podría trabajar. @bvaughn pensamientos?

@barbalex , tienes razón, a mí también me funciona, pero no puedo

@ranneyd Lo acabo de enlace ), pero aparentemente no puede deshabilitar los eventos de desplazamiento, lo único que puede hacer es secuestrar todos los eventos de la rueda del mouse, el tacto y el teclado que pueden causar el desplazamiento y prevenirlos. El enlace (SO) tiene un fragmento de código corto para eso, pero esto lo hace aún más complicado, así que todavía estoy reflexionando sobre ello ...

@alonrbar sí, pedir a los usuarios que hagan esto no funcionará. _Puedes_ darles un enlace directo si _es_ quieres probar: chrome: // flags / # disable -threaded-scrolling

@alonrbar eh esto no es bueno, pero no es TERRIBLE si solo lo estamos aplicando a la cuadrícula que ya hackearemos juntos.

Es solo rueda de desplazamiento, ¿verdad? ¿Las teclas de flecha también lo hacen posible?

Este es un fragmento de la respuesta aceptada aquí: https://stackoverflow.com/questions/4770025/how-to-disable-scrolling-tempontly

function disableScroll() {
  if (window.addEventListener) // older FF
      window.addEventListener('DOMMouseScroll', preventDefault, false);
  document.addEventListener('wheel', preventDefault, {passive: false}); // Disable scrolling in Chrome
  window.onwheel = preventDefault; // modern standard
  window.onmousewheel = document.onmousewheel = preventDefault; // older browsers, IE
  window.ontouchmove  = preventDefault; // mobile
  document.onkeydown  = preventDefaultForScrollKeys;
}

Como puede ver, hay varios eventos que debe manejar y considerar las posibles consecuencias de hacerlo.

Por el momento, como no puedo invertir más tiempo tratando de resolver esto en este momento, decidí usar una solución no virtual (que tampoco es perfecta, pero funciona mejor para mis casos de uso). Puede encontrar el código que uso aquí y aquí (es parte de la biblioteca que escribí que envuelve react-window ).

@alonrbar sí, estoy de acuerdo en que la solución SO es bastante mala.

Sin embargo, encuentro su solución muy interesante. Voy a ver si puedo intentarlo por mi cuenta y hacerlo funcionar con la tabla virtualizada.

@alonrbar, así que hice una implementación no virtualizada que en realidad usa posicionamiento absoluto. Lo único que se desplaza es el contenedor exterior. En el desplazamiento, actualiza los valores superior / izquierdo que se utilizan para colocar en posición absoluta los elementos internos. Así que no scrollTo o scrollTop = ... , lo que descubrí que me estaba causando mucho dolor. Además de eso, las barras de desplazamiento están SIEMPRE en el exterior de la cuadrícula COMPLETA.

Esta cosa que hice puede tener dinámicamente "encabezados congelados" en todos los lados. Es un ejemplo MUY tosco / poc.

Evidentemente, carece de virtualización, lo cual es un problema grave. Desafortunadamente, no sé si puedo envolver Grid de esta biblioteca en él, ya que esta biblioteca básicamente se ejecuta en el desplazamiento y esto elimina el desplazamiento de la cuadrícula interior. No estoy seguro de cuál es el siguiente paso.

https://codesandbox.io/embed/non-virtual-scroll-synced-table-ot467

Creo que la misma lógica de virtualización que alimenta esta biblioteca se puede aplicar a esto.

@ranneyd muy buena implementación!

Me gusta la composición de la cuadrícula dinámica :)

Pero lo más importante es que la idea de utilizar el posicionamiento absoluto en lugar del desplazamiento puede ser la clave para resolver este problema. Al introducir otro div , ha desconectado el evento onscroll del desplazamiento de contenido real, lo que creo que abre nuevas posibilidades.

Si usted podría enganchar en react-window 's render método que puede ser capaz de sustituir las líneas 459 y con su aplicación. Luego, podrá enganchar el evento onscroll y deshabilitar su efecto cuando sea necesario (las barras de desplazamiento siempre se moverán, pero puede controlar el contenido para evitar que cambie).

@alonrbar, así que volví a implementar el algoritmo de virtualización. No es tan bueno, pero mirando el código fuente cuando este Grid llega a la virtualización real, es básicamente el mismo algoritmo. Sin embargo, el rendimiento es realmente decente. Puedo llegar a una cuadrícula de 200x200 sin ningún retraso, y 500x500 con solo un poco. Si mi jefe me deja trabajar más en esto, intentaré implementar el método de renderizado como sugirió, lo cual probablemente sea bastante fácil.

Si está interesado en la cuadrícula que hice, puedo publicarla en algún lugar. Si quieres tomarlo y conectarlo con esta lib, entonces sé mi invitado 😏

@ranneyd sí, me gustaría ver la solución de virtualización que ha creado. No sé si lo usaré, pero sin duda sería interesante :)

Además, estaba tratando de usar su código hoy para implementar los cambios en el método de renderizado como sugerí, pero después de volver a leer su código, noté que en realidad no ha desconectado el desplazamiento como pensé. Lo que quiero decir con eso es que las barras de desplazamiento y el evento onscroll siguen siendo inseparables del desplazamiento de contenido real. Llevé tu idea un paso más allá e implementé un desplazamiento verdaderamente desconectado:

https://codesandbox.io/embed/absolute-position-scrolling-1u7vj

Si observa el código, verá que si eliminamos las propiedades top y left del componente Content , el contenido no se desplaza incluso si las barras de desplazamiento hacer . También he verificado que esta vez los eventos del mouse no están bloqueados. 😂
Creo que ahora es posible usar esta implementación para ignorar el evento onscroll y sincronizar múltiples cuadrículas manualmente. Pero eso, por supuesto, requiere un poco más de trabajo, por lo que tendrá que esperar la próxima vez ...

Esta discusión me dio curiosidad, así que configuré un proyecto para comparar dos ventanas de reacción sincronizadas con desplazamiento Grid s con una MultiGrid virtualizada de reacción, para poder tener una idea de cómo se compara el rendimiento :
https://github.com/bvaughn/react-window-vs-react-virtualized-synced-grids

Intenté hacer que cada uso fuera lo más similar posible. Algunas observaciones iniciales:

  • react-window se ve notablemente más lento en el modo DEV, pero se siente un poco más rápido / con mayor capacidad de respuesta en una compilación de producción. (Es difícil descartar un sesgo de mi parte aquí. La diferencia en la producción es menor).
  • react-window también hace muchas más confirmaciones, ya que el evento de desplazamiento primero actualiza la cuadrícula activa, luego la cuadrícula pasiva a través de una actualización en cascada. Hice un parche en Grid para admitir el paso de un controlador de eventos de desplazamiento "nativo" (React) (que se llamará dentro de la actualización por lotes de React) en lugar del actual onScroll (que se llama durante la confirmación fase). Esto parece un cambio bastante prometedor cuando lo probé localmente, ya que evita las representaciones en cascada separadas, por lo que tal vez cambie el tiempo predeterminado onScroll .

@bvaughn Intenté hacer el código de efecto de desplazamiento de vainilla más básico, con divA -> onScroll -> setState -> ref.scrollTop y todavía no podía sortear esta cosa de desplazamiento con hilos de cromo. Admito que no eludí el estado de reacción y configuré ref.scrollTop dentro del controlador onScroll, pero aparte de eso, no puedo pensar en una forma más básica de hacerlo. Si no puede obtener onScroll -> scrollTop lo suficientemente rápido en el menor número posible de pasos, ¿cómo lo soluciona? ¿Me estoy perdiendo algo por completo? Parece que las cuadrículas no necesitan moverse en función del desplazamiento (o la configuración de scrollTop).

El objetivo siempre es que JavaScript sea lo más rápido posible para que se mantenga al día con el desplazamiento de la gestión de subprocesos.

Admito que no eludí el estado de reacción y configuré ref.scrollTop dentro del controlador onScroll

Para que quede claro, esto tampoco es lo que está haciendo mi repositorio. Solo estoy configurando el desplazamiento de desplazamiento en la cuadrícula pasiva en el mismo controlador de eventos (empaquetado por React) que la cuadrícula activa, por lo que React agrupa sus actualizaciones en un solo render + commit. Para ser honesto, esto probablemente no hace mucha diferencia.

@ranneyd y @bvaughn compartiendo con ustedes mis observaciones hasta ahora:

  1. Muchas implementaciones funcionan lo suficientemente bien en el navegador de escritorio, pero mi caso de uso principal es en realidad en dispositivos móviles y allí veo enormes diferencias de rendimiento.

  2. La sincronización de dos cuadrículas simples no virtuales funciona bastante bien (no es 100% perfecto, pero está lo suficientemente cerca, incluso en dispositivos móviles). Esta es mi implementación ingenua, pero funcional, y un caso de prueba para verlo en acción ( yarn storybook ).

  3. El uso de un desplazamiento "controlado" (como sugerí en este comentario ) mantiene ambas cuadrículas 100% sincronizadas, pero es bastante lento en el escritorio y demasiado lento en el móvil. Esta es mi oportunidad (muy áspera ...): https://github.com/alonrbar/react-window/commit/c39ce7006dbd590d9c640e37f8a1e78826e4688e

    Aun así, estoy dispuesto a ver si la estrategia "controlada" puede funcionar de alguna manera para resolver este problema en una sola lista virtual.

  4. Estaba pensando, ya que dos ya habían sugerido, que tal vez mover el _callPropsCallbacks al _onScroll manejador directamente puede ayudar a reducir el desfase de sincronización al mínimo. EDITAR : lo intenté ahora, realmente no ayuda 😞

  5. De cualquier manera que lo aborde, una buena idea en mi humilde opinión es separar la lógica de virtualización del resto del componente (¿tal vez incluso un gancho?) Para que el procesamiento y el desplazamiento se puedan manejar por separado, comparar fácilmente e incluso cambiar en tiempo de ejecución según el accesorios de componentes. También permitirá exportar esta lógica y permitirá a los usuarios implementar componentes personalizados (como la solución @ranneyd en este comentario ) basados ​​en la misma lógica.

¿Pensamientos?

@alonrbar mi solución, que hace un posicionamiento absoluto, por lo que ninguna de las cuadrículas es la que hace el desplazamiento, funciona muy bien hasta aproximadamente 300x300 donde se vuelve un poco lento (tenga en cuenta que permanece sincronizado el 100% del tiempo, el desplazamiento solo se vuelve un poco lento). En tamaños más grandes, creo que solo se está procesando / mapeando en una gran matriz. Creo que hay varias optimizaciones.
Sin embargo, puedo hacerlo, y no estoy del todo convencido de que tenga nada que ver tanto con la sincronización de desplazamiento como con mi implementación de virtualización bastante simple. Puedo hacer más almacenamiento en caché, por ejemplo, y puedo calcular si una celda debe virtualizarse o no antes para evitar llamar a la función de renderizado (y luego almacenar en caché eso quizás mejor).

No he probado nada en el móvil. Le proporcionaré un código para que lo pruebe en un momento.

Realmente quiero ver la prueba de @bvaughn . Él dice que enganchar directamente al pergamino nativo lo arregla, tú dices que no. Quiero verlo por mi mismo.

En cuanto a colocar la lógica de virtualización en un gancho o una función independiente, se vuelve bastante complicado porque la lógica está intrínsecamente conectada a la vista. También parece que muchos de los ajustes de rendimiento implican almacenamiento en caché y elementos de memorización que pueden ser difíciles de encapsular en un enlace o función, o al menos hasta el punto en que obtiene el mismo rendimiento. Pero miraré lo que tengo y veré cuánta lógica puedo sacar.

PD:
Una cosa en la que pensé que probablemente no funcionaría es hacer una cosa similar a una denuncia + transiciones css. Si solo ejecutamos un evento de desplazamiento cada 100 ms o algo así y animamos el movimiento, podría verse mejor. También puede parecer sustancialmente menos receptivo. Es algo así como lo hicieron en World of Warcraft (si hay un retraso o una latencia alta, harán que un personaje se mueva en línea recta y luego lo corrijan una vez que obtengan la información de adónde fueron).

Realmente quiero ver la prueba de @bvaughn . Él dice que enganchar directamente al pergamino nativo lo arregla, tú dices que no. Quiero verlo por mi mismo.

No dije eso: sonrisa: solo dije que evita un render adicional innecesario y una mutación DOM. No tengo claro cuánto impacto tiene en el rendimiento real. Sin embargo, parece un cambio positivo general, independientemente.

@alonrbar @bvaughn Necesito documentar mi código, pero aquí está la última versión:

https://github.com/ranneyd/synced-table

@alonrbar @bvaughn, así que aquí hay algo divertido que acabo de descubrir:

Mi solución NO funciona en Chrome 75 en una pantalla de macbook. Cuando mis compañeros de trabajo no tenían actualizaciones de Chrome, funcionó. Si usan un monitor externo, funciona. En la pantalla de su computadora portátil, se retrasa.
😩

Hmm ... hay algunas diferencias con los monitores externos, con frecuencia de actualización o escalado. Cuando dices que "no funciona", ¿a qué te refieres específicamente?

Mi error. Me refiero a que el desplazamiento ya no está sincronizado. Si clona mi repositorio y lo ejecuta, puede comparar el monitor externo con la pantalla de la computadora portátil. En el monitor están perfectamente sincronizados. En la pantalla del portátil, los encabezados parpadean (no se actualizan a la misma velocidad que el elemento de desplazamiento).

De hecho, me he rendido y lo estoy intentando con position: sticky . Realmente está funcionando. El soporte del navegador no es del 100%, pero en realidad es mucho mejor de lo que era hace un año.

Existe esta biblioteca que es un polyfill no polyfill que podría funcionar, pero los navegadores a los que nos dirigimos son compatibles con la función.
https://github.com/dollarshaveclub/stickybits

@alonrbar @bvaughn aquí está la última versión. Utiliza position: sticky . Lo explico un poco en el archivo Léame. El código aún debe documentarse.

https://github.com/ranneyd/sticky-table

Mi solución NO funciona en Chrome 75 en una pantalla de macbook. Cuando mis compañeros de trabajo no tenían actualizaciones de Chrome, funcionó. Si usan un monitor externo, funciona. En la pantalla de su computadora portátil, se retrasa.
😩

@ranneyd Hoy se me ocurrió que esto podría estar relacionado con la velocidad de fotogramas. La velocidad de fotogramas para un monitor externo podría ser, por ejemplo, 30 fps, en cuyo caso creo que el navegador podría enviar menos eventos de "desplazamiento" (ya que creo que la mayoría de los navegadores solo envían uno por fotograma / pintura). ¿Quizás es por eso que el retraso es más notable para usted en un monitor externo?

@bvaughn Creo que tiene que ser así. El sistema operativo DICE que está enviando 60 Hz y CREO que las especificaciones del monitor dicen 60 Hz, pero no me sorprendería que alguien mienta 😂

@ranneyd Lamento decir que no estoy muy disponible en este momento, así que no pude echarle un vistazo a su solución, pero de un vistazo corto noté que se ve bastante bien y que ha creado un useVirtual hook y separó elegantemente la lógica del renderizado.
Creo que sería genial si de alguna manera pudiera crear una solicitud de extracción para react-window , usarla allí y tal vez exponer un método renderTable de algún tipo donde pueda conectar su lógica de renderizado. De esta manera, su biblioteca podría simplemente ajustar la ventana de reacción en lugar de reemplazarla. Creo que sería preferible si pudiéramos aprovechar este enfoque y resolver el problema dentro de react-window que ya está bastante probado y extendido.

Por otro lado, creo que si scrollTo usará la manipulación DOM directa (tal vez además de establecer el estado, pero en cualquier caso antes de hacerlo), el resultado de sincronizar dos tablas será más fluido. Si no me equivoco, @bvaughn también estaba sugiriendo algo en esta dirección.

@ranneyd, su solución de tabla adhesiva se ve muy bien y estaría muy feliz de poder usarla. ¿Consideraría lanzar una api utilizable para él en npm ?

@bvaughn @ranneyd

Entonces, después de bastante tiempo, volví recientemente a este tema. Utilizando código e ideas tanto de sus bibliotecas como de las de recyclinglistview y react-virtual-grid , finalmente pude lograr el comportamiento que quería, es decir, una cuadrícula de alto rendimiento con filas y columnas fijas que funciona bien tanto en computadoras de escritorio como en dispositivos móviles. dispositivos (desplazamiento suave sin celdas vacías / blancas).

El TLDR es que utilizo el reciclaje junto con el posicionamiento fijo y la pieza de código más interesante se puede encontrar aquí en este método .

Créditos y más sobre la motivación para escribir otra solución más están aquí . ¡Gracias!

Dejaré un enlace a mi ejemplo de Code Sandbox en funcionamiento aquí en caso de que alguien más se encuentre con este problema en el futuro.
https://codesandbox.io/s/y3pyp85zm1

Muy bien, hasta que te desplace completamente hacia la derecha: entonces los encabezados están desalineados con el contenido (por el ancho de la barra de desplazamiento vertical).
¿Habría encontrado por casualidad una solución a eso?
Gracias

Dejaré un enlace a mi ejemplo de Code Sandbox en funcionamiento aquí en caso de que alguien más se encuentre con este problema en el futuro.
https://codesandbox.io/s/y3pyp85zm1

Muy bien, hasta que te desplace completamente hacia la derecha: entonces los encabezados están desalineados con el contenido (por el ancho de la barra de desplazamiento vertical).
¿Habría encontrado por casualidad una solución a eso?
Gracias

Revelación completa: de hecho, hice mi propia versión desde cero. Tiene su propia virtualización basada en gran medida en esta lib. La razón por la que hice esto fue porque hay un problema en MacBooks y con ciertas banderas de Chrome donde la animación para el desplazamiento ocurre con una sincronización diferente a la de JS. El uso de ScrollSync requería demasiadas llamadas de función transferidas y era demasiado lento. Básicamente, tuve que rehacer la biblioteca con encabezados fijos en su núcleo (no usé position: sticky como dije anteriormente en este hilo. Necesito cargar lo que tengo ahora en algún momento).

Dicho esto, la forma en que soluciono este problema es mediante overflow: scroll para forzar una barra de desplazamiento y luego agregar ese relleno a la última celda, o determino dinámicamente si hay una barra de desplazamiento (y cuál es su ancho) usando un ref y colocando un div invisible dentro de él, midiendo el tamaño de la barra de desplazamiento y eliminando el nodo DOM.

He usado overflow-y: overlay que hace que la barra de desplazamiento se superponga. Desafortunadamente, solo funciona para navegadores webkit.

Ok, gracias por la información. Realmente parece que los encabezados adhesivos son una necesidad tan común, que simplemente agregar la opción de primera fila adhesiva a react-window lo convertiría en un reemplazo de react-virtualized en muchos más casos. ¿No te parece?

@ranneyd gracias de nuevo, seguí tu enfoque y la sincronización de desplazamiento funciona bien.

El desbordamiento oculto en las barras de desplazamiento provoca una desalineación cerca del final de la cuadrícula:

gridalignement

Alguna sugerencia en como arreglar esto?

Ejemplo de CodeSandbox

Gracias por adelantado

Tarde para la fiesta, pero si simplemente agregas una fila a la leftRef y pones su desbordamiento en hidden, básicamente resuelves el pb. (Lo hice también para no tener que sincronizar el leftRef -> main Grid

  const headerRef = React.useRef();
  const leftRef   = React.useRef();

  return <Box classes={{
            root:classes.tableContainer
          }}>
      <AutoSizer>
        {({ height, width }) => (<>
        {/*---------------- LA TABLE -------------*/}
          <Grid
            columnCount={1000}
            columnWidth={100}
            height={height}
            rowCount={1000}
            rowHeight={35}
            width={width}
            onScroll={({ scrollLeft, scrollTop }) => {
              if (leftRef.current) {
                leftRef.current.scrollTo({ scrollTop });
              }
              if (headerRef.current) {
                headerRef.current.scrollTo({ scrollLeft });
              }
            }}
          >
            {({ columnIndex, rowIndex, style }) => (
              <Box style={style} classes={{root:classes.cell}}>
                Item {rowIndex},{columnIndex}
              </Box>
            )}
          </Grid>
          {/*---------------- HEADER -------------*/}
          <Grid
            ref={headerRef}
            outerElementType={React.forwardRef((props, ref) => (
              <div ref={ref}  {...props} style={{...props.style,position:"absolute",overflow:"hidden",top:0,right:0,left:150}} />
            ))}
            columnCount={1001}  /*columns count +1 for scroll problems*/
            columnWidth={100}
            height={60}
            rowCount={1}
            rowHeight={60}
            width={width}
          >
            {({ columnIndex, rowIndex, style }) => (
              <Box style={style} classes={{root:classes.headerCell}}>
                Header {rowIndex},{columnIndex}
              </Box>
            )}
          </Grid>  
          {/*---------------- LEFT COL -------------*/}
          <Grid
            ref={leftRef}
            outerElementType={React.forwardRef((props, ref) => (
              <div ref={ref}  {...props} style={{...props.style,position:"absolute",overflow:"hidden",top:60,left:0}} />
            ))}
            columnCount={1}
            columnWidth={150}
            height={height}
            rowCount={251} /** add 1 for scroll problems at the end */
            rowHeight={140}
            width={150}
          >
            {({ columnIndex, rowIndex, style }) => (
              <Box style={style} classes={{root:classes.headerCell}}>
                Left {rowIndex},{columnIndex}
              </Box>
            )}
          </Grid>  
        </>)}
      </AutoSizer>
    </Box>
¿Fue útil esta página
0 / 5 - 0 calificaciones