React-window: Reagiere benutzerdefinierte Bildlaufleiste mit Reaktionsfenster

Erstellt am 17. Dez. 2018  ·  35Kommentare  ·  Quelle: bvaughn/react-window

Ich verwende react-custom-scrollbar und möchte es in FixedSizeList .

Ich habe die Lösung für dieses Problem unter react-virtualized überprüft: https://github.com/bvaughn/react-virtualized/issues/692#issuecomment -339393521

Aber der Code löst einen Fehler aus: Uncaught TypeError: Cannot read property 'handleScrollEvent' of undefined beim Scrollen in dieser Funktion:

  handleScroll = ({ target }) => {
    const { scrollTop, scrollLeft } = target;

    const { Grid: grid } = this.List;

    grid.handleScrollEvent({ scrollTop, scrollLeft });
  }

Ich habe ref={ instance => { this.List = instance; } } für die Komponente fixedSixe <List hinzugefügt.

💬 question

Hilfreichster Kommentar

Ein besserer Ansatz wäre, Scrollbars als outerElementType

const CustomScrollbars = ({ onScroll, forwardedRef, style, children }) => {
  const refSetter = useCallback(scrollbarsRef => {
    if (scrollbarsRef) {
      forwardedRef(scrollbarsRef.view);
    } else {
      forwardedRef(null);
    }
  }, []);

  return (
    <Scrollbars
      ref={refSetter}
      style={{ ...style, overflow: "hidden" }}
      onScroll={onScroll}
    >
      {children}
    </Scrollbars>
  );
};

const CustomScrollbarsVirtualList = React.forwardRef((props, ref) => (
  <CustomScrollbars {...props} forwardedRef={ref} />
));

// ...

<FixedSizeList
  outerElementType={CustomScrollbarsVirtualList}
  {...rest}
/>

Beispiel https://codesandbox.io/s/vmr1l0p463

Alle 35 Kommentare

Diese Bibliothek und react-virtualized sind völlig unterschiedliche Implementierungen. Allgemeine Techniken, die Sie bei react-virtualized -Problemen sehen, können häufig nützliche Hinweise zur Vorgehensweise geben, aber bestimmte Implementierungsdetails wie diese sind nicht anwendbar. (In react-virtualized schmückt List eine Grid -Komponente. In react-window sie separate Komponenten für eine bessere Leistung und Größe.)

Ich weiß nicht, ob so etwas wie react-custom-scrollbar mit react-window funktionieren würde, da ich es nie ausprobiert habe. Um ehrlich zu sein, bin ich nicht sehr daran interessiert, diese zu unterstützen, da ich denke, dass benutzerdefinierte Bildlaufleisten im Allgemeinen eine schlechte Idee sind, da sie sich auf die Leistung auswirken. Also werde ich dieses Problem schließen.

Wir können uns aber weiterhin darüber unterhalten, wenn Sie weitere Fragen haben 😄

Ich werde etwas darüber herausfinden, ich bin daran interessiert, react-window weil es leicht ist und mein Problem löst. Auch wenn ich keine benutzerdefinierte Bildlaufleiste verwenden möchte, aber es liegt nicht in meiner Hand, wir benötigen ein benutzerdefiniertes Design für die Bildlaufleiste.

Im schlimmsten Fall verwende ich react-virtualized .
Vielen Dank :)

@ Rahul-Sagore Haben Sie am Ende benutzerdefinierte Bildlaufleisten erhalten, die mit react-window funktionieren?

Nein, dafür habe ich keine Zeit bekommen. Müssen überprüfen und herausfinden.

Ich bin mir nicht sicher, ob https://codesandbox.io/s/00nw2w1jv

Ein besserer Ansatz wäre, Scrollbars als outerElementType

const CustomScrollbars = ({ onScroll, forwardedRef, style, children }) => {
  const refSetter = useCallback(scrollbarsRef => {
    if (scrollbarsRef) {
      forwardedRef(scrollbarsRef.view);
    } else {
      forwardedRef(null);
    }
  }, []);

  return (
    <Scrollbars
      ref={refSetter}
      style={{ ...style, overflow: "hidden" }}
      onScroll={onScroll}
    >
      {children}
    </Scrollbars>
  );
};

const CustomScrollbarsVirtualList = React.forwardRef((props, ref) => (
  <CustomScrollbars {...props} forwardedRef={ref} />
));

// ...

<FixedSizeList
  outerElementType={CustomScrollbarsVirtualList}
  {...rest}
/>

Beispiel https://codesandbox.io/s/vmr1l0p463

Ich finde es toll, wie viele fortgeschrittene Dinge mit outerElementType oder innerElementType

Ich versuche, die Bildlaufposition am unteren Rand des Reaktionsfensters zu ermitteln.
Ist es möglich? Hast du irgendwelche Ideen dafür? Ich hätte gerne ein Code- und Box- Beispiel.

Sie sollten in der Lage sein, die verfügbaren Ref-Requisiten zu verwenden, um die Liste nach ihren scrollHeight @rufoot zu fragen.

Ich habe versucht, scrollHeight in den Requisiten zu verwenden. Aber ich denke, da ist kein scrollHeight -Eigentum drin.
@bvaughn Hast du eine Idee dazu?

https://codesandbox.io/s/github/bvaughn/react-window/tree/master/website/sandboxes/scrolling-to-a-list-item

scrollToRow200Auto = () => { this.listRef.current.scrollToItem(200); console.log("current position = ", this.listRef.current.props.scrollHeight) };

aktuelle Position = undefiniert

https://codesandbox.io/embed/jzo2lool2y

Wenn Sie unten im Reaktionsfenster nach unten scrollen, sehen Sie eine Warnung.

@piecyk Ich habe deine Lösung mit outerElementType ausprobiert und es ist super verzögert, hast du es gekämpft ?: D.

Ich habe es mit react-scrollbars-custom versucht. Es ist weniger nachlässig, aber immer noch nachlässig. Hat jemand Probleme mit Verzögerungen bei der Verwendung benutzerdefinierter Bildlaufleisten?

@ jancama2 hat es nicht gekämpft. Kannst du Code Sandbox teilen, wenn es nachlässt?

@rufoot so etwas https://codesandbox.io/s/4zjwwq98j4 ?

Ich verwende Ihre Implementierung, erhalte jedoch die Fehlermeldung, dass forwardedRef keine Funktion ist.

Das Problem in meiner App, das ich zu lösen versuche, ist, dass ich eine große Liste von Elementen habe. Wenn ich auf eines klicke, leitet die App zu einer anderen Route um, sodass ich die Bildlaufposition (wahrscheinlich in Redux) und verfolgen muss Stellen Sie dann die Bildlaufposition nach der Routenänderung ein.

@piecyk haben Sie versucht, es mit dem InfiniteLoader von react-window-infinite-loader kombinieren.
Ich benutze react-scrollbars-custom und es scheint den unendlichen Lader zu brechen :(

Mein Code:

<InfiniteLoader isItemLoaded={isItemLoaded} itemCount={items.total} loadMoreItems={loadMoreItems}>
      {({
        onItemsRendered,
        ref,
      }: {
        onItemsRendered: (props: ListOnItemsRenderedProps) => any;
        ref: React.Ref<any>;
      }) => (
        <List
          height={TABLE_HEIGHT}
          width={width}
          itemCount={items.total}
          itemSize={ROW_HEIGHT}
          onItemsRendered={onItemsRendered}
          itemData={{ items }}
          ref={ref}
          outerElementType={Scrollbar}
        >
          {Row}
        </List>
      )}
    </InfiniteLoader>

Update: Ich glaube nicht, dass es mit der unendlichen Bildlaufkomponente zusammenhängt. Ich kann es nicht mit der benutzerdefinierten Bildlaufleiste zum Laufen bringen.

Ich würde mich freuen, wenn jemand die Implementierung für React-Scrollbars-Custom mit mir teilen kann

@ranihorev Müssen Sie dieses Paket verwenden? Der oben freigegebene React -Custom-Scrollbars und funktioniert gut (außer beim Erstellen einer Referenz, die aus irgendeinem Grund in meinem Code null ist).

Ok, ich muss noch einmal fragen. @piecyk Ich verwende Ihr Codebeispiel von oben (wo Sie auf @simjes geantwortet
<TreeRoot onDragOver={e => this.onDragOver(e)} > <AutoSizer> {({ height, width }) => ( <List treeRef={this.props.treeRef} width={width} height={height} itemSize={index => rowHeights[index]} itemCount={nodeTree.length} itemData={{ nodeTree, expandedNodes, nodesFetching, nodesFetchingFailed, selectedNode, selectedNodeId, resetRowHeight: this.resetRowHeight, }} outerElementType={TreeScrollbar} outerRef={this.props.outerRef} onScroll={({ scrollOffset, scrollUpdateWasRequested }) => { if (scrollUpdateWasRequested) { console.log('scrollOffset: ', scrollOffset); } }} > {Node} </List> )} </AutoSizer> </TreeRoot>

Ich implementiere die CustomScrollbars und CustomScrollbarsVirtualList genauso wie Sie, aber die Referenz ist immer null. Ich habe versucht, die Referenz in der Komponente, die ich mit dem AutoSizer verwende, sowie in der übergeordneten Komponente zu erstellen, aber sie ist immer null. Ich würde mich sehr über Hilfe freuen, wenn ich verstehe, warum. Wie oben erwähnt, muss ich die Bildlaufposition verfolgen und sie bei einer Routenänderung in meiner App festlegen können, damit die Liste nicht nach oben zurückspringt.

@ChristopherHButler Ich verwende dieses Paket bereits mehrmals in meinem Code, daher möchte ich lieber dasselbe Paket verwenden. Die Lösung ist ziemlich ähnlich, ruft jedoch keine neuen Daten ordnungsgemäß ab

@ChristopherHButler wirklich schwer zu sagen, ohne ein Code Sandbox Beispiel des Problems, können Sie etwas teilen?
Grundsätzlich besteht die Idee hier darin, den Ref aus den React-Custom-Scrollbars so zu setzen, dass das React-Fenster angezeigt wird

AutoSizer oder VariableSizeList funktionieren wie in diesem Beispiel mit diesem Ansatz
https://codesandbox.io/s/react-window-custom-scrollbars-t4352

@ranihorev können Sie auch Code Sandbox Beispiel teilen?

@piecyk Ich erstelle ein einfaches (und kaputtes) Beispiel:
https://codesandbox.io/s/bvaughnreact-window-fixed-size-list-vertical-64kzh?fontsize=14

Ich habe auch andere Variationen ausprobiert (z. B. den Ref als Scroller-Wrapper festlegen), aber keinen Erfolg ...

Vielen Dank!

@ranihorev Dieser Ansatz wäre unabhängig von der Implementierung eines benutzerdefinierten

react-scrollbars-custom bietet eine schönere API mit Render-Requisiten-Mustern, sodass Sie so etwas wie https://codesandbox.io/s/bvaughnreact-window-react-scrollbars-custom-pjyxs ausführen können

Habe keine Profilerstellung durchgeführt, hoffe das hilft 👍

@piecyk Ich arbeite hier mit Ihrer Demo: https://codesandbox.io/s/4zjwwq98j4 . Ich möchte den onScrollStop verwenden, um eine Aktion in Redux auszulösen, damit ich die Bildlaufposition festlegen kann (eine Idee aus diesem Thread: https://github.com/malte-wessel/react-custom-scrollbars/issues/146, aber onScrollStart und onScrollStop scheint in meinem Code oder in Ihrer Demo nicht zu funktionieren. Wissen Sie, warum dies so ist? Gibt es eine Möglichkeit, Requisiten aus der Liste an die Bildlaufleisten zu übergeben?

@ChristopherHButler Ich denke, Ihre beste Option ist es, den Kontext zu verwenden, um die Requisiten zu übergeben, so etwas wie

https://codesandbox.io/s/bvaughnreact-window-fixed-size-list-vertical-v2-usi1m

--- Bearbeitet
Wenn Sie die gesamte Liste bei Routenänderungen aushängen, können Sie die Aktion auslösen. Last scrollOffset kann bei ref gespeichert und bei jedem Bildlaufwechsel mit onScroll: function von VariableSizeList aktualisiert werden. Dann müssen Sie keine Requisiten an Bildlaufleisten übergeben

@piecyk das ist
Übrigens scheint alles, was Sie brauchen, die Funktion onScroll (die ich verpasst habe), und es besteht überhaupt keine Notwendigkeit, einen Schiedsrichter weiterzuleiten.
https://codesandbox.io/s/bvaughnreact-window-react-scrollbars-custom-99dn1

@piecyk Entschuldigung, ich hätte erwähnen sollen, dass meine Implementierung der VariableSizeList in einen AutoSizer eingeschlossen ist und die Komponente, die sie rendert, eine klassenbasierte Komponente ist, sodass ich nicht sicher bin, ob ich den Kontext verwenden kann. Vielleicht kann ich deshalb onScrollStop nicht zum Laufen bringen?

Schnelles Update:
Ich habe den Komponentenstatus hinzugefügt und ihn auf Scroll gesetzt.

`
Klasse BaseTree erweitert Component {
...
state = {scrollPosition: 0,};
...
listRef = React.createRef ();
OuterRef = React.createRef ();
...
rendern {
const {node, nodeFetching, nodeFetchingFailed, expandNodes, selectedNode} = this.props;
Rückkehr (

{({Höhe, Breite}) => (
ref = {this.listRef}
className = "Liste"
OuterRef = {this.outerRef}
OuterElementType = {TreeScrollbar}
width = {width}
height = {height}
onScroll = {({scrollOffset}) => this.setState ({scrollPosition: scrollOffset})}
itemSize = {index => rowHeights [index]}
itemCount = {nodeTree.length}
itemData = {{
nodeTree,
expandNodes,
nodeFetching,
nodeFetchingFailed,
selectedNode,
selectedNodeId,
resetRowHeight: this.resetRowHeight,
}}
>
{Knoten}

)}

);
}}

const mapStateToProps = state => ({
scrollPosition: selectors.getTreeScrollPosition (state),
});

const mapDispatchToProps = dispatch => ({
setTreeScrollPosition: position => dispatch (action.setTreeScrollPosition ({position})),
});

Standardverbindung exportieren (mapStateToProps, mapDispatchToProps) (BaseTree);
`

dann sende ich auf componentWillUnmout die Aktion aus, um die Position in Redux wie folgt festzulegen:

componentWillUnmount() { this.props.setTreeScrollPosition(this.state.scrollPosition); }

Mein einziges Problem ist jetzt, die Bildlaufposition festlegen zu können, wenn die Komponente erneut bereitgestellt wird. Ich habe versucht, auf die scrollTop-Methode auf this.listRef wie folgt zuzugreifen:

this.listRef.current.scrollTop() aber ich erhalte die Fehlermeldung, dass es sich nicht um eine Funktion handelt. Ich bin nicht sicher, welche Eigenschaft (this.listRef oder this.outerRef) ich zum Festlegen der Bildlaufposition oder in welcher Methode verwenden kann. Ich dachte, ich könnte es in der KomponenteDidMount des BaseTree wie folgt einstellen:
componentDidMount() { const { scrollPosition } = this.props; if (this.listRef.current) { // console.log('initializing scroll position to : ', scrollPosition); this.listRef.current.scrollTop(scrollPosition); } }
Jede Hilfe, um dies zum Laufen zu bringen, wäre sehr dankbar!
edit: Es tut mir sehr leid, aber mein Code scheint in diesem Editor nicht richtig formatiert zu sein, also entschuldige ich mich dafür :(

@ranihorev forward ref zum Reagieren des Fensters ist erforderlich, damit Funktionen wie scrollTo, scrollToItem funktionieren

Der @ ChristopherHButler- Kontext sollte genau so funktionieren, wie sie aus Dokumenten erstellt wurden

Der Kontext bietet eine Möglichkeit, Daten durch den Komponentenbaum zu leiten, ohne Requisiten auf jeder Ebene manuell weitergeben zu müssen.

Aber ja, wie bereits erwähnt, müssen Sie die Requisiten nicht übergeben, wenn Sie die gesamte Komponente beim Routenwechsel aushängen. Würde scrollPosition im Status speichern, da Sie es nicht benötigen, während das Scrollen ausgeführt wird, weniger erneutes Rendern, speichern Sie es in ref.

onScroll={({ scrollOffset }) => this.setState({ scrollPosition: scrollOffset })}

// to 

lastScrollOffsetRef = React.createRef(0);

<List
  onScroll={({ scrollOffset }) => {
    this.lastScrollOffsetRef.current = scrollOffset
  }}
  // ...rest
/>

// then in 
componentWillUnmount() { 
  this.props.setTreeScrollPosition(this.lastScrollOffsetRef.current); 
}

In Bezug auf outerRef ist dies in Ihrem Fall nicht erforderlich. listRef.current.scrollTo ist die richtige Methode, die Sie verwenden möchten. Hmm, Ihre Idee, die Methode in componentDidMount aufzurufen, ist richtig und sollte funktionieren.

https://codesandbox.io/s/bvaughnreact-window-fixed-size-list-vertical-80zo7

Es sieht aus wie ein Timing-Problem beim Festlegen von Refs. Möglicherweise verzögert AutoSizer das Rendern der Liste, wodurch die Datei this.listRef.current undefiniert wird.

Die hackige Option besteht darin, mit setTimeout aus dem Reaktionslebenszyklus auszubrechen und den Ref beim nächsten Tick aufzurufen

componentDidMount() {
  window.setTimeout(() => {
    if (this.listRef.current) {
      this.listRef.current.scrollTo(this.props.scrollPosition);
    }
  }, 0);
}

@piecyk vielen Dank für die Hilfe, ich weiß das wirklich zu schätzen :)

Danke @piecyk Das scheint zu funktionieren. Nach der Routenänderung flackert das Mounten leicht, aber ich kann damit leben :) Ich habe auch einen anderen Hack versucht, bei dem der Klassenname verwendet wurde, um die scrollPosition zu übergeben und scrollTop im refSetter von CustomScrollbars festzulegen:
if (scrollbarsRef) { forwardedRef(scrollbarsRef.view); // HACK scrollbarsRef.scrollTop(className); }
Es scheint gut zu funktionieren.
Nochmals vielen Dank für all Ihre Hilfe, es ist sehr dankbar! 🙌🏻

Leider funktionieren alle Beispiele in dieser Ausgabe nicht richtig (manchmal reagiert die Maus nicht mehr auf das Scrollen).

Aber dieses Beispiel funktioniert perfekt!

@ranihorev Dieser Ansatz wäre unabhängig von der Implementierung eines benutzerdefinierten

react-scrollbars-custom bietet eine schönere API mit Render-Requisiten-Mustern, sodass Sie so etwas wie https://codesandbox.io/s/bvaughnreact-window-react-scrollbars-custom-pjyxs ausführen können

Ich habe es mit react-scrollbars-custom versucht. Es ist weniger nachlässig, aber immer noch nachlässig. Hat jemand Probleme mit Verzögerungen bei der Verwendung benutzerdefinierter Bildlaufleisten?

Hast du es geschafft? Was ist die endgültige Lösung?

Wenn dies jemandem hilft, habe ich es geschafft, dass es mit OverlayScrollbars funktioniert . Sie müssen nur das Bildlaufereignis an das entsprechende Element übergeben. Das ist wie:

const Overflow = ({ children, onScroll }) => {
  const ofRef = useRef(null);

  useEffect(() => {
    const el = ofRef.current.osInstance().getElements().viewport;

    if (onScroll) el.addEventListener('scroll', onScroll);

    return () => {
      if (onScroll) el.removeEventListener('scroll', onScroll);
    };
  }, [onScroll]);

  return (
    <OverlayScrollbarsComponent
      options={options}
      ref={ofRef}
    >
      {children}
    </OverlayScrollbarsComponent>
  );
};

Und dann in Ihrer virtualisierten Komponente:

<FixedSizeGrid
  {...props}
   outerElementType={Overflow}
>

Ich benutze dies mit FixedSizeGrid aber es sollte für Listen genauso funktionieren.

Ich hoffe es hilft.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen