React-window: réagit la barre de défilement personnalisée avec react-window

Créé le 17 déc. 2018  ·  35Commentaires  ·  Source: bvaughn/react-window

J'utilise react-custom-scrollbar et j'aimerais l'intégrer avec FixedSizeList .

J'ai vérifié la solution à ce problème sur react-virtualized : https://github.com/bvaughn/react-virtualized/issues/692#issuecomment -339393521

Mais le code lance une erreur: Uncaught TypeError: Cannot read property 'handleScrollEvent' of undefined sur le défilement, dans cette fonction:

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

    const { Grid: grid } = this.List;

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

J'ai ajouté ref={ instance => { this.List = instance; } } sur le composant fixedSixe <List .

💬 question

Commentaire le plus utile

Une meilleure approche serait de passer Scrollbars comme 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}
/>

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

Tous les 35 commentaires

Cette bibliothèque et react-virtualized sont des implémentations complètement différentes. Les techniques générales que vous voyez sur les problèmes react-virtualized peuvent souvent fournir des conseils utiles sur la façon d'aborder quelque chose, mais des détails d'implémentation spécifiques comme celui-ci ne sont pas applicables. (Dans react-virtualized , List décore un composant Grid . Dans react-window ce sont des composants séparés, pour de meilleures performances et une meilleure taille.)

Je ne sais pas si quelque chose comme react-custom-scrollbar fonctionnerait avec react-window puisque je ne l'ai jamais essayé. Pour être honnête, ce n'est pas quelque chose qui m'intéresse beaucoup, car je pense que les barres de défilement personnalisées sont généralement une mauvaise idée en raison de leur impact sur les performances. Je vais donc clore ce problème.

Mais nous pouvons continuer à en discuter si vous avez des questions complémentaires 😄

Je vais trouver quelque chose à ce sujet, je suis intéressé à utiliser react-window car il est léger et résout mon problème. Même je n'aime pas utiliser la barre de défilement personnalisée, mais ce n'est pas dans ma main, nous avons besoin d'une conception personnalisée pour la barre de défilement.

J'utiliserai react-virtualized , dans le pire des cas.
Merci :)

@ Rahul-Sagore avez-vous fini par obtenir des barres de défilement personnalisées fonctionnant avec react-window ?

Non, je n'ai pas eu le temps pour ça. Besoin de vérifier et de comprendre.

Je ne sais pas si @bvaughn approuve cela, mais cela semble fonctionner: https://codesandbox.io/s/00nw2w1jv

Une meilleure approche serait de passer Scrollbars comme 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}
/>

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

J'adore le nombre de choses avancées possibles en utilisant outerElementType ou innerElementType

J'essaye de détecter la position de défilement au bas de la fenêtre de réaction.
C'est possible? Avez-vous des idées pour cela? J'aimerais avoir un exemple de

Vous devriez pouvoir utiliser les accessoires de référence disponibles pour demander à la liste ses scrollHeight @rufoot.

J'ai essayé d'utiliser scrollHeight dans les accessoires. Mais je pense qu'il n'y a pas scrollHeight propriété
@bvaughn Avez-vous une idée à ce sujet?

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

position actuelle = indéfinie

@rufoot quelque chose comme ça https://codesandbox.io/s/4zjwwq98j4 ?

https://codesandbox.io/embed/jzo2lool2y

Lorsque vous faites défiler vers le bas en bas de la fenêtre de réaction, vous pouvez voir une alerte.

@piecyk J'ai essayé votre solution avec outerElementType et elle est super lente, l'avez-vous testée?: D

Je l'ai essayé avec react-scrollbars-custom . C'est moins lent mais toujours lent. Quelqu'un a-t-il eu des problèmes avec les décalages en utilisant des barres de défilement personnalisées?

@ jancama2 ne l'a pas testé 😂Pouvez-vous partager Code Sandbox quand il devient lent?

@rufoot quelque chose comme ça https://codesandbox.io/s/4zjwwq98j4 ?

J'utilise votre implémentation mais j'obtiens une erreur indiquant que forwardedRef n'est pas une fonction.

Le problème dans mon application que j'essaie de résoudre est que j'ai une grande liste d'éléments et lorsque je clique sur l'un d'entre eux, l'application se redirige vers une autre route, je dois donc garder une trace de la position de défilement (probablement en redux) et puis définissez la position de défilement après le changement d'itinéraire.

@piecyk avez-vous essayé de le combiner avec InfiniteLoader à partir de react-window-infinite-loader .
J'utilise react-scrollbars-custom et il semble casser le chargeur infini :(

Mon 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>

Mise à jour: je ne pense pas que cela soit lié au composant de défilement infini. Je n'arrive pas à le faire fonctionner avec la barre de défilement personnalisée.

J'apprécierais que quelqu'un puisse partager l'implémentation de react-scrollbars-custom avec moi

@ranihorev avez-vous besoin d'utiliser ce package? Le stylo de code @piecyk partagé ci-dessus utilise des barres de défilement

Ok, j'ai besoin de demander à nouveau. @piecyk J'utilise votre exemple de code ci-dessus (où vous avez répondu à @simjes ). au lieu d'utiliser une liste de taille fixe cependant, j'utilise le sizer automatique pour envelopper un VariableSizeList et spécifier le externalElementType. Quelque chose comme ça:
<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>

J'implémente les CustomScrollbars et CustomScrollbarsVirtualList de la même manière que vous, mais la référence est toujours nulle. J'ai essayé de créer la référence dans le composant que j'utilise l'AutoSizer ainsi que dans son parent, mais il est toujours nul. J'apprécierais vraiment de l'aide pour comprendre pourquoi. Comme je l'ai mentionné ci-dessus, je dois garder une trace de la position de défilement et pouvoir la définir sur un changement d'itinéraire dans mon application afin que la liste ne revienne pas en haut.

@ChristopherHButler J'utilise déjà ce package plusieurs fois dans mon code, donc je préfère continuer à utiliser le même package. La solution est assez similaire, mais elle ne récupère pas de nouvelles données correctement

@ChristopherHButler est vraiment difficile à dire sans exemple de code Sandbox du problème, pouvez-vous partager quelque chose?
L'idée de base ici est de définir la référence à partir des barres de défilement react-custom-afin que la fenêtre react

AutoSizer ou VariableSizeList fonctionnera avec cette approche, comme dans cet exemple
https://codesandbox.io/s/react-window-custom-scrollbars-t4352

@ranihorev pouvez-vous également partager l'exemple de Code Sandbox?

@piecyk Je crée un exemple simple (et cassé):
https://codesandbox.io/s/bvaughnreact-window-fixed-size-list-vertical-64kzh?fontsize=14

J'ai aussi essayé d'autres variantes (par exemple en définissant la ref pour être le scroller wrapper) mais sans succès ...

Merci!

@ranihorev cette approche serait la même quelle que soit l'implémentation de défilement personnalisé. Ils doivent définir une référence au même élément qui est responsable du défilement et gérer cet événement de défilement.

react-scrollbars-custom fournit une API plus agréable en utilisant un modèle d'accessoires de rendu afin que vous puissiez faire quelque chose comme https://codesandbox.io/s/bvaughnreact-window-react-scrollbars-custom-pjyxs

Je n'ai fait aucun profilage, j'espère que cela vous aidera 👍

@piecyk Je travaille avec votre démo ici: https://codesandbox.io/s/4zjwwq98j4 . Je veux utiliser onScrollStop pour envoyer une action dans redux afin que je puisse définir la position de défilement (une idée de ce fil: https://github.com/malte-wessel/react-custom-scrollbars/issues/146 mais onScrollStart et onScrollStop ne semble pas fonctionner dans mon code ou dans votre démo. Savez-vous pourquoi? Existe-t-il un moyen de passer des accessoires de la liste aux barres de défilement?

@ChristopherHButler Je pense que votre meilleure option est d'utiliser le contexte pour passer les accessoires, quelque chose comme

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

--- Modifié
Si vous démontez toute la liste lorsque la route change, vous pouvez distribuer l'action alors, Last scrollOffset peut être stocké sur ref et mis à jour à chaque changement de défilement en utilisant le onScroll: function de VariableSizeList, vous n'avez pas besoin de passer les accessoires aux barres de défilement

@piecyk c'est génial, merci!
btw, il semble que tout ce dont vous avez besoin est la fonction onScroll (que j'ai manquée) et il n'est pas du tout nécessaire de transmettre une référence.
https://codesandbox.io/s/bvaughnreact-window-react-scrollbars-custom-99dn1

@piecyk désolé, j'aurais dû mentionner que mon implémentation de VariableSizeList est enveloppée dans un AutoSizer et le composant qui le rend est un composant basé sur une classe, donc je ne suis pas sûr de pouvoir utiliser le contexte. C'est peut-être pourquoi je ne parviens pas à faire fonctionner onScrollStop?

Mise à jour rapide:
J'ai ajouté l'état du composant et l'ai défini sur défilement.

''
La classe BaseTree étend le composant {
...
état = {scrollPosition: 0,};
...
listRef = React.createRef ();
externalRef = React.createRef ();
...
rendre {
const {nodes, nodesFetching, nodesFetchingFailed, ExpandNodes, selectedNode} = this.props;
revenir (

{({hauteur, largeur}) => (
ref = {this.listRef}
className = "Liste"
externalRef = {this.outerRef}
externalElementType = {TreeScrollbar}
width = {width}
hauteur = {hauteur}
onScroll = {({scrollOffset}) => this.setState ({scrollPosition: scrollOffset})}
itemSize = {index => rowHeights [index]}
itemCount = {nodeTree.length}
itemData = {{
nodeTree,
ExpandNodes,
nœudsRécupération,
nodesFetchingFailed,
selectedNode,
selectedNodeId,
resetRowHeight: this.resetRowHeight,
}}
>
{Nœud}

)}

);
}}

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

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

exporter la connexion par défaut (mapStateToProps, mapDispatchToProps) (BaseTree);
''

puis sur componentWillUnmout je distribue l'action pour définir la position dans redux comme ceci:

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

mon seul problème maintenant est de pouvoir définir la position de défilement lorsque le composant se remonte. J'ai essayé d'accéder à la méthode scrollTop sur this.listRef comme ceci:

this.listRef.current.scrollTop() mais j'obtiens une erreur indiquant que ce n'est pas une fonction. Je ne sais pas quelle propriété (this.listRef ou this.outerRef) je peux utiliser pour définir la position de défilement ou dans quelle méthode. Je pensais que je pourrais le définir dans le componentDidMount de BaseTree comme suit:
componentDidMount() { const { scrollPosition } = this.props; if (this.listRef.current) { // console.log('initializing scroll position to : ', scrollPosition); this.listRef.current.scrollTop(scrollPosition); } }
Toute aide pour que cela fonctionne serait grandement appréciée!
edit: Je suis vraiment désolé mais mon code ne semble pas être formaté correctement dans cet éditeur donc je m'en excuse :(

@ranihorev forward ref to react-window est nécessaire pour que des fonctions comme scrollTo, scrollToItem fonctionnent

Le contexte

Le contexte fournit un moyen de transmettre des données à travers l'arborescence des composants sans avoir à passer manuellement les accessoires à tous les niveaux.

Mais oui, comme mentionné précédemment, si vous démontez tout le composant lors du changement de route, il n'est pas nécessaire de passer les accessoires. Ne stockerait pas scrollPosition sur l'état car vous n'en avez pas besoin pendant le défilement, moins de rendus, stockez-le sur 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); 
}

Concernant outerRef , ce n'est pas nécessaire dans votre cas. listRef.current.scrollTo est la bonne méthode que vous souhaitez utiliser. Hmm, votre idée d'appeler la méthode dans componentDidMount est correcte et devrait fonctionner,

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

ressemble à un problème de synchronisation lors de la définition des refs, peut-être que l'AutoSizer retarde le rendu de la liste qui rend le this.listRef.current indéfini,

L'option de piratage consiste à interrompre le cycle de vie de réaction avec setTimeout et à appeler la référence au prochain tick

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

@piecyk merci beaucoup pour l'aide, je l'apprécie vraiment :)

Merci @piecyk Cela semble fonctionner. Il y a un léger scintillement lors du montage après le changement de route mais je peux vivre avec :) J'ai aussi essayé un autre hack qui consistait à utiliser le className pour passer le scrollPosition et définir scrollTop dans le refSetter de CustomScrollbars:
if (scrollbarsRef) { forwardedRef(scrollbarsRef.view); // HACK scrollbarsRef.scrollTop(className); }
Cela semble bien fonctionner.
Merci encore pour toute votre aide, c'est grandement apprécié !! 🙌🏻

Malheureusement, tous les exemples de ce problème ne fonctionnent pas correctement (parfois la souris cesse de répondre au défilement).

Mais cet exemple fonctionne parfaitement!

@ranihorev cette approche serait la même quelle que soit l'implémentation de défilement personnalisé. Ils doivent définir une référence au même élément qui est responsable du défilement et gérer cet événement de défilement.

react-scrollbars-custom fournit une API plus agréable en utilisant un modèle d'accessoires de rendu afin que vous puissiez faire quelque chose comme https://codesandbox.io/s/bvaughnreact-window-react-scrollbars-custom-pjyxs

Je l'ai essayé avec react-scrollbars-custom . C'est moins lent mais toujours lent. Quelqu'un a-t-il eu des problèmes avec les décalages en utilisant des barres de défilement personnalisées?

L'avez-vous fait? Quelle est la solution finale?

Si cela aide quelqu'un, j'ai réussi à le faire fonctionner avec OverlayScrollbars . Il vous suffit de transmettre l'événement scroll à l'élément correspondant. C'est ainsi:

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

Et puis, dans votre composant virtualisé:

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

J'utilise ceci avec FixedSizeGrid mais cela devrait fonctionner de la même manière pour les listes.

J'espère que cela aide.

Cette page vous a été utile?
0 / 5 - 0 notes