React-window: Kompatibilität mit ScrollSync

Erstellt am 8. Nov. 2018  ·  52Kommentare  ·  Quelle: bvaughn/react-window

Ich habe einen Anwendungsfall, bei dem ich ein Raster erstellen muss, das feste Kopfzeilen hat, die oben angeheftet bleiben. In React-Virtualized könnte dies erreicht werden, indem zwei Rasterkomponenten erstellt werden (eine für Kopfzeilen, eine für den Rasterkörper) und die Bildlaufposition so synchronisiert wird, dass das horizontale Bildlauf des Kopfzeilenrasters mit dem Hauptraster synchronisiert wird.

Ich weiß, dass Sie versuchen, das Gewicht des Reaktionsfensters sowohl in Bezug auf die Paketgröße als auch auf die konzeptionelle Komplexität zu reduzieren, um zusätzliche Funktionen als separate Pakete zu erstellen. Das ist eine Richtung, der ich zustimme. Ich denke, dass die ScrollSync-Komponente von React-Virtualized extrahiert oder angepasst werden könnte, um mit React-Window zu arbeiten. Um dies jedoch zu tun, müsste React-Window Props für scrollLeft und scrollTop akzeptieren, damit der Scroll-Offset des Header-Grids direkt verwaltet werden kann.

Ist dies ein Anwendungsfall, den Sie gerne unterstützen würden? Wenn nicht, haben Sie einen Tipp, in welche Richtung ich gehen sollte, um dies selbst umzusetzen?

Vielen Dank für Ihre Arbeit an dieser Bibliothek. Als jemand, der seit einigen Jahren mit React-Virtualized arbeitet, schätze ich die Einfachheit des Einstiegs mit React-Window und wie performant es für mich ohne viel manuelles Eingreifen war.

Hilfreichster Kommentar

Das macht absolut Sinn. Danke für den Vorschlag! Ich habe es zum Laufen gebracht und es ist eigentlich ganz einfach einzurichten. So einfach, dass es definitiv kein eigenständiges Paket rechtfertigt. Vielleicht ein Beispiel in den Dokumenten, aber das ist Ihre Entscheidung, nehme ich an. Ich werde hier einen Link zu meinem funktionierenden Code-Sandbox-Beispiel hinterlassen, falls jemand in Zukunft über dieses Problem stolpert.

https://codesandbox.io/s/y3pyp85zm1

TLDR - füge eine Referenz in das Kopfzeilenraster ein und füge diese in das Textkörperraster ein.

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

Alle 52 Kommentare

Erstmal danke für die netten Worte und das positive Feedback. Es freut mich zu hören, dass das React-Window bei Ihnen bisher gut funktioniert hat!

Ich stimme zu, dass eine Komponente wie ScrollSync als eigenständiges Paket veröffentlicht werden könnte, das von React-Window abhängt, aber ich möchte sie diesem Projekt nicht hinzufügen, da sie nicht zum Windowing gehört.

In Bezug auf Ihre spezielle Frage zu Scroll-Requisiten ist dies keine Änderung, die ich an dem Projekt vornehmen würde, da ich nach der Verwendung mit React-Virtualized festgestellt habe, dass es einige schwerwiegende Nachteile hat. Ich habe tatsächlich im React-Blog darüber geschrieben, wenn Sie neugierig sind:

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

Um ein ähnliches Synchronisierungsverhalten zu erreichen, können Sie die imperative Scroll-API verwenden, die von React-Window angeboten wird. Sie müssten diese Methoden nur aus Commit-Lebenszyklen aufrufen, anstatt Props zu übergeben.

Hoffentlich macht das Sinn, aber wenn nicht, können Sie gerne weitere Fragen stellen!

Das macht absolut Sinn. Danke für den Vorschlag! Ich habe es zum Laufen gebracht und es ist eigentlich ganz einfach einzurichten. So einfach, dass es definitiv kein eigenständiges Paket rechtfertigt. Vielleicht ein Beispiel in den Dokumenten, aber das ist Ihre Entscheidung, nehme ich an. Ich werde hier einen Link zu meinem funktionierenden Code-Sandbox-Beispiel hinterlassen, falls jemand in Zukunft über dieses Problem stolpert.

https://codesandbox.io/s/y3pyp85zm1

TLDR - füge eine Referenz in das Kopfzeilenraster ein und füge diese in das Textkörperraster ein.

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

Danke für den Link! Das ist sehr nachdenklich.

Am Do, 8. November 2018, 13:09 schrieb Reagan Keeler < [email protected] :

Das macht absolut Sinn. Danke für den Vorschlag! Ich habe es zum Laufen gebracht,
und es ist eigentlich ganz einfach einzurichten. So einfach, dass es definitiv nicht geht
ein eigenständiges Paket garantieren. Vielleicht ein Beispiel in den Dokumenten, aber das ist dein
anrufen, nehme ich an. Ich werde hier einen Link zu meinem funktionierenden Code-Sandbox-Beispiel hinterlassen
falls noch jemand über dieses Problem stolpert.

https://codesandbox.io/s/y3pyp85zm1

TLDR - füge eine Referenz in das Kopfzeilenraster ein und füge diese in das Textkörperraster ein.

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


Sie erhalten dies, weil Sie den Status Öffnen/Schließen geändert haben.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/bvaughn/react-window/issues/86#issuecomment-437156749 ,
oder den Thread stumm schalten
https://github.com/notifications/unsubscribe-auth/AABznTUunzEIs6bVQfVsz7T21L2-Pkkoks5utJ17gaJpZM4YVMd7
.

Für jeden, der darüber gestolpert ist, hatte ich in react-virtualized ein oben/rechts/unten/links eingefrorenes Supergrid mit Scroll-Synchronisierung, das ich gerade in diese Bibliothek migriert habe. Ich sage, dass die obige Lösung mindestens genauso leistungsfähig, wenn nicht sogar besser und wesentlich weniger schmerzhaft ist als die Verwendung von ScrollSync in der anderen Bibliothek.

Es passt auch ganz gut zum neuen useRef Reaktions-Hook (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 });
    }
  }}
  ...
/>

Schön! Danke fürs Teilen von @ranneyd!

Es passt auch ganz gut zum neuen useRef Reaktions-Hook (https://reactjs.org/docs/hooks-reference.html#useref).

Danke @ranneyd

Wäre es möglich, ein funktionierendes Beispiel zu teilen?

@ranneyd Nochmals vielen Dank, ich bin Ihrem Ansatz gefolgt und die Scroll-Synchronisierung funktioniert

Der versteckte Überlauf auf den Bildlaufleisten verursacht eine Fehlausrichtung am Ende des Rasters:

gridalignement

Irgendwelche Vorschläge, wie man das beheben kann?

CodeSandbox-Beispiel

Danke im Voraus

@carlosagsmendes liegt an der Bildlaufleiste. Auf der linken Seite machen Sie die Höhe zur Höhe minus der Größe der Bildlaufleiste. Um geräteübergreifende Konsistenz zu erreichen, kodiere ich die Größe der Bildlaufleiste manuell mit CSS. Wenn Ihr Inhalt so dynamisch ist, dass eine Bildlaufleiste vorhanden sein kann oder nicht, tun Sie etwas wie "Anzahl Elemente * Größe Elemente < Breite (Sie sollten alle diese Werte genau dort haben)" und ändern Sie dann die Höhe entsprechend.

Um geräteübergreifende Konsistenz zu erreichen, kodiere ich die Größe der Bildlaufleiste manuell mit CSS.

FWIW das Paket dom-helpers hat eine praktische scrollbarSize Funktion , die Ihnen die Breite auf dem aktuellen Gerät mitteilt. Könnte schöner sein als Hard-Coding.

@bvaughn ja! Das habe ich vergessen zu erwähnen. Bei uns hat es jedoch nicht funktioniert. Ich denke, das Problem war, dass wir bereits hartcodierte Bildlaufleisten erstellt haben und das hat es verwirrt

Danke dir. Ich werde es versuchen!

Funktioniert das mit horizontaler VariableSizeList? Ich habe es versucht, aber mein Browser friert ein.

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

Es funktioniert gut mit VariableSizeGrid, aber mein Header hinkt beim Scrollen etwas hinterher.

@ajaymore Also die Kopfzeile Probleme , wenn Sie das Scrollrad auf einem Mac verwenden. MacOSX hat dieses eingebaute natürliche Scrolling-Ding, bei dem es das Scrollen interpoliert, um es "glatter" zu machen. Die Bildwiederholrate ist dabei tatsächlich schneller als die Animationsbildrate in Chrome. Daher wird das Element, das Sie scrollen, animiert, bevor V8/ScrollSync Zeit hat, das DOM zu aktualisieren. Dies ist eine technische Einschränkung, für die ich keine Lösung gefunden habe.

Fun Fact: Wenn Sie die Bildlaufleiste im Browser manuell verwenden (wie das altmodische Scrollen durch Ziehen des kleinen Dings), funktioniert es völlig problemlos. Die Interpolationssache ist in das Scrollrad/Trackpad integriert

@ajaymore Also die Kopfzeile Probleme , wenn Sie das Scrollrad auf einem Mac verwenden. MacOSX hat dieses eingebaute natürliche Scrolling-Ding, bei dem es das Scrollen interpoliert, um es "glatter" zu machen. Die Bildwiederholrate ist dabei tatsächlich schneller als die Animationsbildrate in Chrome. Daher wird das Element, das Sie scrollen, animiert, bevor V8/ScrollSync Zeit hat, das DOM zu aktualisieren. Dies ist eine technische Einschränkung, für die ich keine Lösung gefunden habe.

Fun Fact: Wenn Sie die Bildlaufleiste im Browser manuell verwenden (wie das altmodische Scrollen durch Ziehen des kleinen Dings), funktioniert es völlig problemlos. Die Interpolationssache ist in das Scrollrad/Trackpad integriert

@ranneyd Danke für die schnelle Antwort. Ich stimme zu, es ist eine Einschränkung. Es wird auf den meisten Geräten gut funktionieren, also kein großes Problem.

@bvaughn hast du schon mal mit position: sticky ? Ich habe es mir eine Weile nicht angesehen und ich weiß, dass die Browserunterstützung spärlich ist, aber ich frage mich, ob es eine Möglichkeit gibt, es in Browsern zu nutzen, in denen es unterstützt wird ...

Ich habe das gleiche Problem wie @ajaymore ,

@alonrbar Ich habe das gleiche Problem auf einem Windows-PC.

Ich habe gerade einen Workaround gefunden, der für mich funktioniert.
Die allgemeine Idee besteht darin, ein "Schattenraster" zu erstellen, das sich vor dem ursprünglichen Raster verbirgt und sein onScroll-Ereignis stiehlt und es dann verwendet, um das Originalraster sowie jedes andere erforderliche Raster manuell zu scrollen. Es verlangsamt die Leistung ein wenig, hält aber alle Raster gut synchronisiert, sodass Sie den Kompromiss in Betracht ziehen müssen.

Der Code sieht so aus;

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;
`;

Dann in einer anderen Datei:

<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 das ist faszinierend! Ich werde das bald ausprobieren.

Es sieht so aus, als ob das Schattengitter über dem echten Gitter lebt, ja? Wenn ja, werden Klickereignisse im "echten Raster" nicht funktionieren, oder? Ich nehme an, Sie könnten etwas Hacky mit Klickereignissen + x/y-Koordinaten machen und das irgendwie auf das Hauptraster anwenden.

Auch @barbalex re: schlägt auch in Windows fehl

Es könnte tatsächlich eine Chromflagge sein. Sehen Sie nach, ob sich etwas in chrome://flags befindet. Möglicherweise hat Microsoft auch etwas Ähnliches implementiert.

Das Problem scheint sicherlich damit zusammenzuhängen, dass das Scrollen schneller "geglättet" wird als der Animationsrahmen des Browsers. Wenn Sie besorgt sind, dass es sich nur um eine Leistung / Verzögerung handelt, versuchen Sie, eine sehr einfache Scroll-Synchronisierungsimplementierung zu erstellen (wobei beim Scrollen eines div die Scrollposition eines anderen festgelegt wird) und sehen Sie, ob Sie das gleiche Problem haben. Vielleicht sogar in reinem Vanille-JS, um React als Schuldigen zu eliminieren

Ahhh @ranneyd du hast recht, es blockiert Klickereignisse ...

Ich denke, die Threaded-Scrolling-Funktion in chrome://flags hat einen großen Einfluss: Wenn ich sie ausschalte, ist das Scrollen mit dem Mausrad für mich ungefähr so ​​​​flüssig wie das Scrollen mit der Scrollbar.

2019-07-15_00h03_42

@alonrbar was ist damit:

Fügen Sie dem Hauptraster einen Scroll-Handler hinzu. Darin machen Sie e.preventDefault(); oder etwas, um das eigentliche Scrollen zu verhindern. Dann schauen Sie sich das Ereignis an, um herauszufinden, wie viel es gescrollt hätte, was dies tut, um die anderen synchronisierten Dinge zu verschieben, aber dann verwenden Sie es, um dasselbe Element manuell zu scrollen. Anstatt also mit A zu scrollen und dann diese Informationen zu verwenden, um B zu scrollen, fangen Sie das Scrollen auf A ab, brechen es ab und verwenden es dann, um B und A selbst zu scrollen. Funktioniert das?

Ich bin nicht von einem Computer zu testen. Ziemlich hacky, aber ich könnte arbeiten. @bvaughn Gedanken?

@barbalex du hast recht, bei mir funktioniert es auch, aber ich kann nicht alle meine Benutzer dazu bringen, Chrome-Flags ein- und auszuschalten 😔

@ranneyd Ich habe es mir gerade angeschaut und ein bisschen mit dem Quellcode ( Link ) gefummelt, aber anscheinend können Sie Scroll-Ereignisse nicht deaktivieren sie verhindern. Der Link (SO) hat dafür ein kurzes Code-Snippet, aber das macht es noch hackiger, also denke ich immer noch darüber nach ...

@alonrbar Ja, die Benutzer dazu ://flags/# disable -threaded-scrolling

@alonrbar eh das ist nicht gut, aber es ist nicht SCHRECKLICH, wenn wir es nur auf das Raster anwenden, werden wir bereits zusammen hacken.

Es ist nur ein Scrollrad, oder? Machen die Pfeiltasten das auch?

Dies ist ein Ausschnitt aus der akzeptierten Antwort hier: https://stackoverflow.com/questions/4770025/how-to-disable-scrolling-temporarily

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

Wie Sie sehen, müssen Sie mit mehreren Ereignissen umgehen und mögliche Konsequenzen berücksichtigen.

Da ich im Moment nicht mehr Zeit investieren kann, um dieses Problem zu lösen, habe ich mich für eine nicht virtuelle Lösung entschieden (die auch nicht perfekt ist, aber für meine Anwendungsfälle besser funktioniert). Sie können den Code, den ich verwende, hier und hier finden (er ist Teil der Bibliothek, die ich geschrieben habe und die react-window ).

@alonrbar ja, ich stimme zu, dass die SO-Lösung ziemlich mies ist.

Deine Lösung finde ich aber sehr interessant. Ich werde sehen, ob ich es selbst versuchen kann, es mit der virtualisierten Tabelle zum Laufen zu bringen.

@alonrbar also habe ich eine nicht virtualisierte Implementierung erstellt, die tatsächlich absolute Positionierung verwendet. Das einzige, was scrollt, ist der äußere Behälter. Beim Scrollen aktualisiert es die oberen/linken Werte, die verwendet werden, um die inneren Elemente absolut zu positionieren. Also kein scrollTo oder scrollTop = ... , was mir viel Kummer bereitete. Darüber hinaus befinden sich die Bildlaufleisten IMMER außerhalb des GESAMTEN Rasters.

Dieses Ding, das ich gemacht habe, kann auf allen Seiten dynamisch "eingefrorene Header" haben. Es ist ein SEHR grobes Beispiel/poc.

Offensichtlich fehlt es an Virtualisierung, was ein ernstes Problem ist. Leider weiß ich nicht, ob ich Grid aus dieser Bibliothek darin einpacken kann, da diese Lib im Grunde beim Scrollen läuft und dies das Scrollen im Inneren des Grids eliminiert. Nicht sicher, was der nächste Schritt ist.

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

Ich denke, die gleiche Virtualisierungslogik, die diese Bibliothek antreibt, kann darauf angewendet werden.

@ranneyd sehr coole Umsetzung!

Ich mag die dynamische Gitterzusammensetzung :)

Aber am wichtigsten ist, dass die Idee, die absolute Positionierung anstelle des Scrollens zu verwenden, der Schlüssel zur Lösung dieses Problems sein kann. Durch die Einführung eines weiteren div haben Sie das Ereignis onscroll vom eigentlichen Scrollen des Inhalts getrennt, was meiner Meinung nach neue Möglichkeiten eröffnet.

Wenn Sie sich in die react-window render -Methode von könnten, können Sie möglicherweise die Zeilen 459 und weiter durch Ihre Implementierung ersetzen. Dann können Sie das Onscroll-Ereignis einhaken und seinen Effekt bei Bedarf deaktivieren (die Bildlaufleisten bewegen sich immer noch, aber Sie können den Inhalt steuern, um zu verhindern, dass er sich ändert).

@alonrbar also habe ich den Virtualisierungsalgorithmus tatsächlich neu implementiert. Es ist nicht so gut, aber wenn man sich den Quellcode ansieht, wenn dieses Grid die eigentliche Virtualisierung erreicht, ist es im Grunde das gleiche Alg. Die Leistung ist aber eigentlich ordentlich. Ich komme ohne Verzögerung zu einem 200x200-Raster und 500x500 mit nur wenig. Wenn mein Chef mich weiter daran arbeiten lässt, werde ich versuchen, die von Ihnen vorgeschlagene Implementierung der Rendermethode durchzuführen, was wahrscheinlich eigentlich ziemlich einfach ist.

Wenn Sie an dem von mir erstellten Raster interessiert sind, kann ich es irgendwo posten. Wenn du es nehmen und mit dieser Bibliothek verbinden willst, dann sei mein Gast 😏

@ranneyd ja, ich möchte die von Ihnen erstellte Virtualisierungslösung sehen. Ich weiß nicht, ob ich es verwenden werde, aber es wäre auf jeden Fall interessant :)

Außerdem habe ich heute versucht, Ihren Code zu verwenden, um die von mir vorgeschlagenen Änderungen an der Render-Methode zu implementieren, aber nachdem ich Ihren Code erneut gelesen habe, habe ich festgestellt, dass Sie das Scrollen nicht so getrennt haben, wie ich es mir vorgestellt habe. Was ich damit meine ist, dass die Scrollbars und das onscroll Ereignis immer noch untrennbar mit dem eigentlichen Inhalts-Scrolling verbunden sind. Ich habe Ihre Idee noch einen Schritt weitergeführt und eine wirklich unzusammenhängende Schriftrolle implementiert:

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

Wenn Sie den Code anschauen werde werden Sie sehen , dass , wenn wir die entfernen top und left Eigenschaften von Content Komponente der Inhalt nicht selbst wenn die Scrollbalken gescrollt tun . Ich habe auch überprüft, dass dieses Mal Mausereignisse nicht blockiert werden. 😂
Ich glaube, es ist jetzt möglich, diese Implementierung zu verwenden, um das Ereignis onscroll zu ignorieren und mehrere Raster manuell zu synchronisieren. Aber das erfordert natürlich noch etwas mehr Arbeit, also muss es auf das nächste Mal warten...

Diese Diskussion hat mich neugierig gemacht, also habe ich ein Projekt eingerichtet, um zwei scroll-synchronisierte Reak-Fenster Grid s mit einem reaktiv-virtualisierten MultiGrid , damit ich ein Gefühl dafür bekommen kann, wie die Perf im Vergleich ist :
https://github.com/bvaughn/react-window-vs-react-virtualized-synced-grids

Ich habe versucht, jede Verwendung so ähnlich wie möglich zu machen. Einige erste Beobachtungen:

  • Reagieren-Fenster sieht im DEV-Modus merklich langsamer aus, fühlt sich aber in einem Produktions-Build etwas schneller / reaktionsschneller an. (Hier ist eine Voreingenommenheit meinerseits schwer auszuschließen. Der Produktionsunterschied ist geringer.)
  • React-Window macht auch viel mehr Commits, da das Scroll-Event zuerst das aktive Grid aktualisiert, dann das passive Grid über ein kaskadierendes Update. Ich habe einen Patch für Grid , um die Übergabe eines "nativen" (React) Scroll-Ereignishandlers (der innerhalb des Batch-Updates von React aufgerufen wird) anstelle des aktuellen onScroll (der während des Commits aufgerufen wird) zu unterstützen Phase). Dies scheint eine ziemlich vielversprechende Änderung zu sein, als ich es lokal getestet habe, da es separate kaskadierende Renderings vermeidet, also werde ich vielleicht einfach das Standard-Timing von onScroll ändern.

@bvaughn Ich habe versucht, den einfachsten Vanilla-Scroll-Effekt-Code mit divA -> onScroll -> setState -> ref.scrollTop zu erstellen, und es kam immer noch nicht um dieses Chrome-Thread-Scrolling-Ding herum. Ich habe zugegebenermaßen den Reaction State nicht umgangen und ref.scrollTop innerhalb des onScroll-Handlers gesetzt, aber abgesehen davon kann ich mir keine einfachere Möglichkeit vorstellen, dies zu tun. Wenn Sie onScroll -> scrollTop nicht schnell genug in möglichst wenigen Schritten erreichen können, wie können Sie das Problem beheben? Übersehe ich etwas total? Es scheint, als müssten sich die Raster nicht aufgrund des Scrollens (oder der Einstellung von scrollTop) bewegen.

Das Ziel ist immer, dass JavaScript so schnell wie möglich ist, damit es mit dem Thread, der das Scrollen verwaltet, Schritt hält.

Ich habe zugegebenermaßen den Reaction State nicht umgangen und ref.scrollTop innerhalb des onScroll-Handlers gesetzt

Nur um es klarzustellen, das ist auch nicht das, was mein Repo tut. Ich stelle nur den Scroll-Offset im passiven Raster im gleichen (React-wrapped) Event-Handler wie das aktive Raster ein, sodass React ihre Aktualisierungen in einem einzigen Render + Commit stapelt. Das macht wahrscheinlich keinen _viel_ Unterschied, um ehrlich zu sein.

@ranneyd und @bvaughn teilen mit Ihnen meine

  1. Viele Implementierungen funktionieren in Desktop-Browsern gut genug, aber mein Hauptanwendungsfall sind tatsächlich mobile Geräte, und da sehe ich enorme Leistungsunterschiede.

  2. Die Synchronisierung zweier einfacher nicht virtueller Raster funktioniert ziemlich gut (nicht 100% perfekt, aber nah genug, sogar auf dem Handy). Dies ist meine naive, aber funktionierende Implementierung und ein Testfall , um sie in Aktion zu sehen ( yarn storybook ).

  3. Die Verwendung eines "kontrollierten" Scrollens (wie ich in diesem Kommentar vorgeschlagen habe ) hält beide Raster zu 100% synchron, ist aber auf dem Desktop ziemlich langsam und auf dem Handy völlig zu langsam. Das ist mein Crack (sehr grob...): https://github.com/alonrbar/react-window/commit/c39ce7006dbd590d9c640e37f8a1e78826e4688e

    Trotzdem bin ich versucht zu sehen, ob die "kontrollierte" Strategie irgendwie funktionieren kann, um dieses Problem auf einer einzigen virtuellen Liste zu lösen.

  4. Ich dachte, wie Sie beide bereits vorgeschlagen hatten, dass das direkte Verschieben des _callPropsCallbacks in den _onScroll Handler möglicherweise dazu beitragen kann, die Synchronisationsverzögerung auf ein Minimum zu reduzieren. EDIT - habe es jetzt gerade probiert, hilft nicht wirklich 😞

  5. In jedem Fall ist es IMHO eine gute Idee, die Virtualisierungslogik vom Rest der Komponente (vielleicht sogar ein Hook?) Komponenten Requisiten. Es wird auch möglich sein, diese Logik zu exportieren und Benutzern die Implementierung benutzerdefinierter Komponenten (z. B. @ranneyd- Lösung in diesem Kommentar ) basierend auf derselben Logik zu ermöglichen.

Die Gedanken?

@alonrbar Meine Lösung, die eine absolute Positionierung
Ich kann es jedoch tun, und ich bin nicht ganz davon überzeugt, dass es etwas mit der Scroll-Synchronisierung zu tun hat, sondern mit meiner ziemlich einfachen Virtualisierungsimplementierung. Ich kann zum Beispiel mehr Caching durchführen, und ich kann berechnen, ob eine Zelle virtualisiert werden sollte oder nicht, um den Aufruf der Render-Funktion überhaupt zu vermeiden (und das dann vielleicht besser zwischenspeichern).

Auf dem Handy habe ich nichts getestet. Ich werde Ihnen in Kürze einen Code zum Ausprobieren zur Verfügung stellen.

Ich möchte mir unbedingt den ansehen . Er sagt, dass das direkte Einhaken in die native Schriftrolle das Problem behebt, Sie sagen, dass dies nicht der Fall ist. Ich möchte es selbst sehen.

Die Virtualisierungslogik in einen Hook oder eine unabhängige Funktion zu ziehen, wird ziemlich knifflig, da die Logik untrennbar mit der Ansicht verbunden ist. Es scheint auch, dass viele der Leistungsoptimierungen Caching- und Memoisierungs-Zeug beinhalten, die möglicherweise schwer in einen Hook oder eine Funktion zu kapseln sind, oder zumindest in einem Ausmaß, in dem die gleiche Leistung erzielt wird. Aber ich werde mir ansehen, was ich habe und sehen, wie viel von der Logik ich herausholen kann.

PS:
Eine Sache, an die ich gerade gedacht habe, die wahrscheinlich nicht funktionieren würde, ist, eine denunzierende Sache + CSS-Übergänge zu machen. Wenn wir nur alle 100 ms ein Scroll-Ereignis oder so ausführen und die Bewegung animieren, sieht es vielleicht besser aus. Es kann auch wesentlich weniger reaktionsschnell erscheinen. Es ist ein bisschen so, wie sie World of Warcraft gemacht haben (wenn es eine Verzögerung oder eine hohe Latenz gibt, bewegen sie einen Charakter in einer geraden Linie und korrigieren ihn dann, sobald sie die Informationen darüber erhalten haben, wohin sie tatsächlich gegangen sind).

Ich möchte mir unbedingt den ansehen . Er sagt, dass das direkte Einhaken in die native Schriftrolle das Problem behebt, Sie sagen, dass dies nicht der Fall ist. Ich möchte es selbst sehen.

Das habe ich nicht gesagt :smile: Ich sagte nur, dass es unnötiges Rendern und DOM-Mutationen vermeidet. Wie stark sich das auf die tatsächliche Leistung auswirkt, ist mir unklar. Es scheint jedoch eine insgesamt positive Veränderung zu sein.

@alonrbar @bvaughn Ich muss meinen Code tatsächlich dokumentieren, aber hier ist die neueste Version:

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

@alonrbar @bvaughn Also hier ist eine lustige Sache, die ich gerade entdeckt habe:

Meine Lösung funktioniert NICHT in Chrome 75 auf einem Macbook-Bildschirm. Als meine Kollegen Chrome nicht aktualisiert hatten, funktionierte es. Wenn sie einen externen Monitor verwenden, funktioniert es. Auf ihrem Laptop-Bildschirm verzögert es.
😩

Hmm...es gibt einige Unterschiede bei externen Monitoren, bei der Bildwiederholfrequenz oder Skalierung. Wenn Sie sagen, es funktioniert nicht, was meinen Sie konkret?

Mein Fehler. Ich meine, das Scrollen wird nicht mehr synchronisiert. Wenn Sie mein Repo klonen und ausführen, können Sie den externen Monitor mit dem Laptop-Bildschirm vergleichen. Auf dem Monitor sind sie perfekt synchron. Auf dem Laptop-Bildschirm flackern die Header (nicht mit der gleichen Geschwindigkeit wie das scrollende Element aktualisieren).

Ich habe tatsächlich aufgegeben und versuche es mit position: sticky . Es funktioniert tatsächlich. Die Browserunterstützung ist nicht 100%, aber tatsächlich viel besser als noch vor einem Jahr.

Es gibt diese Bibliothek, die ein Nicht-Polyfill-Polyfill ist, das möglicherweise funktioniert, aber die Browser, die wir ansprechen, unterstützen diese Funktion.
https://github.com/dollarshaveclub/stickybits

@alonrbar @bvaughn hier ist die neueste Version. Es verwendet position: sticky . Ich erkläre es ein wenig in der Readme. Der Code muss noch dokumentiert werden.

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

Meine Lösung funktioniert NICHT in Chrome 75 auf einem Macbook-Bildschirm. Als meine Kollegen Chrome nicht aktualisiert hatten, funktionierte es. Wenn sie einen externen Monitor verwenden, funktioniert es. Auf ihrem Laptop-Bildschirm verzögert es.
😩

@ranneyd Mir ist heute eingefallen, dass dies möglicherweise mit der Bildrate zusammenhängt. Die Bildrate für einen externen Monitor könnte zB 30 fps betragen, in diesem Fall denke ich, dass der Browser weniger "Scroll"-Ereignisse aussendet (da ich denke, dass die meisten Browser nur eines pro Bild/Paint senden). Vielleicht ist die Verzögerung deshalb auf einem externen Monitor für Sie stärker wahrnehmbar?

@bvaughn Ich denke, das muss es sein. Das Betriebssystem SAGT, dass es 60 Hz sendet und ich denke, die Spezifikationen des Monitors sagen 60 Hz, aber es würde mich nicht überraschen, wenn jemand lügt

@ranneyd Es tut mir leid zu sagen, dass ich im useVirtual hast
Ich denke, es wäre großartig, wenn Sie irgendwie einen Pull-Request für react-window erstellen, ihn dort verwenden und vielleicht eine Art renderTable Methode bereitstellen könnten, in die Sie Ihre Renderlogik einfügen könnten. Auf diese Weise könnte Ihre Bibliothek das Reaktionsfenster einfach umhüllen, anstatt es zu ersetzen. Ich glaube, es wäre wünschenswert, wenn wir diesen Ansatz nutzen und das Problem in react-window lösen könnten, das bereits ziemlich kampferprobt und weit verbreitet ist.

In einer anderen Hinsicht denke ich, dass, wenn scrollTo direkte DOM-Manipulation verwendet (vielleicht zusätzlich zum Setzen des Status, aber auf jeden Fall vorher), das Ergebnis der Synchronisierung zweier Tabellen reibungsloser wird. Wenn ich mich nicht irre, hat @bvaughn auch etwas in diese Richtung vorgeschlagen.

@ranneyd deine Sticky-Table-Lösung sieht wirklich toll aus und ich würde mich sehr freuen, sie verwenden zu können. Würden Sie in Betracht ziehen, eine verwendbare API dafür auf npm ?

@bvaughn @ranneyd

Also, nach einiger Zeit bin ich vor kurzem auf dieses Thema zurückgekommen. Mit Code und Ideen sowohl aus Ihren Bibliotheken als auch aus recyclerlistview und React-Virtual-Grid konnte ich endlich das gewünschte Verhalten erreichen, d. h. ein Hochleistungs-Grid mit festen Zeilen und Spalten, das sowohl auf dem Desktop als auch auf dem Handy gut funktioniert Geräte (sanftes Scrollen ohne leere/weiße Felder).

Die TLDR davon ist, dass ich Recycling zusammen mit Sticky Positioning verwende und das interessanteste Stück Code hier in dieser Methode gefunden werden kann .

Credits und mehr zur Motivation zum Schreiben einer weiteren Lösung finden Sie hier . Vielen Dank!

Ich werde hier einen Link zu meinem funktionierenden Code-Sandbox-Beispiel hinterlassen, falls jemand in Zukunft über dieses Problem stolpert.
https://codesandbox.io/s/y3pyp85zm1

Sehr schön, bis man ganz nach rechts scrollt: dann sind die Kopfzeilen falsch am Inhalt ausgerichtet (um die Breite des vertikalen Scrollbalkens).
Hätten Sie dafür zufällig eine Lösung gefunden?
Vielen Dank

Ich werde hier einen Link zu meinem funktionierenden Code-Sandbox-Beispiel hinterlassen, falls jemand in Zukunft über dieses Problem stolpert.
https://codesandbox.io/s/y3pyp85zm1

Sehr schön, bis man ganz nach rechts scrollt: dann sind die Kopfzeilen falsch am Inhalt ausgerichtet (um die Breite des vertikalen Scrollbalkens).
Hätten Sie dafür zufällig eine Lösung gefunden?
Vielen Dank

Vollständige Offenlegung: Ich habe tatsächlich meine eigene Version von Grund auf neu erstellt. Es hat seine eigene Virtualisierung, die stark auf dieser Bibliothek basiert. Der Grund dafür war, dass auf MacBooks und bei bestimmten Chrome-Flags ein Problem auftritt, bei dem die Animation zum Scrollen mit einem anderen Timing als bei JS erfolgt. Die Verwendung von ScrollSync erforderte zu viele weitergegebene Funktionsaufrufe und war zu langsam. Ich musste die Bibliothek im Grunde mit klebrigen Headern im Kern neu erstellen (ich habe nicht position: sticky wie ich früher in diesem Thread gesagt habe. Ich muss das hochladen, was ich jetzt habe).

Das heißt, ich umgehe dieses Problem entweder mit overflow: scroll , um eine Bildlaufleiste zu erzwingen und dann diese Auffüllung zur letzten Zelle hinzuzufügen, oder ich bestimme dynamisch, ob es eine Bildlaufleiste gibt (und wie breit sie ist) mit a ref und fügt ein unsichtbares div ein, misst die Größe der Bildlaufleiste und löscht den DOM-Knoten.

Ich habe overflow-y: overlay , wodurch die Bildlaufleiste überlagert wird. Leider funktioniert es nur für Webkit-Browser.

Okay danke für die Info. Es scheint wirklich so zu sein, dass Sticky Header so häufig benötigt werden, dass das bloße Hinzufügen der sticky first row-Option zu react-window es in vielen weiteren Fällen zu einem Ersatz für React-Virtualized machen würde. Meinst du nicht?

@ranneyd Nochmals vielen Dank, ich bin Ihrem Ansatz gefolgt und die Scroll-Synchronisierung funktioniert

Der versteckte Überlauf auf den Bildlaufleisten verursacht eine Fehlausrichtung am Ende des Rasters:

gridalignement

Irgendwelche Vorschläge, wie man das beheben kann?

CodeSandbox-Beispiel

Danke im Voraus

Zu spät zur Party, aber wenn Sie einfach eine Zeile zur leftRef hinzufügen und den Überlauf auf hidden setzen, lösen Sie im Grunde das pb. (Ich habe es auch gemacht, um die leftRef -> main Grid nicht synchronisieren zu müssen

  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>
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

carolin913 picture carolin913  ·  3Kommentare

davalapar picture davalapar  ·  3Kommentare

Kizmar picture Kizmar  ·  3Kommentare

maynir picture maynir  ·  4Kommentare

lifeisaloha picture lifeisaloha  ·  3Kommentare