React-window: reaccionar barra de desplazamiento personalizada con reaccionar-ventana

Creado en 17 dic. 2018  ·  35Comentarios  ·  Fuente: bvaughn/react-window

Estoy usando react-custom-scrollbar y me gustaría integrarlo con FixedSizeList .

He comprobado la solución a este problema en react-virtualized : https://github.com/bvaughn/react-virtualized/issues/692#issuecomment -339393521

Pero el código arroja un error: Uncaught TypeError: Cannot read property 'handleScrollEvent' of undefined en el desplazamiento, en esta función:

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

    const { Grid: grid } = this.List;

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

He agregado ref={ instance => { this.List = instance; } } en el componente FixedSixe <List .

💬 question

Comentario más útil

Un mejor enfoque sería pasar Scrollbars como 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}
/>

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

Todos 35 comentarios

Esta biblioteca y react-virtualized son implementaciones completamente diferentes. Las técnicas generales que ve en los problemas de react-virtualized menudo pueden proporcionar sugerencias útiles sobre cómo abordar algo, pero los detalles de implementación específicos como este no son aplicables. (En react-virtualized , List decora un componente Grid . En react-window son componentes separados, para un mejor rendimiento y tamaño).

No sé si algo como react-custom-scrollbar funcionaría con react-window ya que nunca lo he probado. Para ser honesto, no es algo que me interese mucho en apoyar, ya que creo que las barras de desplazamiento personalizadas son generalmente una mala idea debido a su impacto en el rendimiento. Así que voy a cerrar este tema.

Pero podemos seguir charlando sobre él si tiene preguntas de seguimiento 😄

Descubriré algo sobre esto, estoy interesado en usar react-window porque es liviano y resuelve mi problema. Incluso a mí no me gusta usar la barra de desplazamiento personalizada, pero no está en mi mano, necesitamos un diseño personalizado para la barra de desplazamiento.

Usaré react-virtualized , en el peor de los casos.
Gracias :)

@ Rahul-Sagore, ¿terminaste obteniendo barras de desplazamiento personalizadas que funcionan con react-window ?

No, no tuve tiempo para eso. Necesito comprobarlo y averiguarlo.

No estoy seguro si @bvaughn aprueba esto, pero parece funcionar: https://codesandbox.io/s/00nw2w1jv

Un mejor enfoque sería pasar Scrollbars como 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}
/>

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

Me encanta cuántas cosas avanzadas son posibles usando outerElementType o innerElementType

Estoy tratando de detectar la posición de desplazamiento en la parte inferior de la ventana de reacción.
¿Es posible? ¿Tienes alguna idea para eso? Me gustaría tener un ejemplo de

Debería poder usar los accesorios de referencia disponibles para pedirle a la lista su scrollHeight @rufoot.

Intenté usar scrollHeight en los accesorios. Pero creo que no hay propiedad scrollHeight en eso.
@bvaughn ¿Tienes alguna idea al respecto?

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

posición actual = indefinida

@rufoot algo como esto https://codesandbox.io/s/4zjwwq98j4 ?

https://codesandbox.io/embed/jzo2lool2y

Cuando se desplaza hacia abajo en la parte inferior de la ventana de reacción, puede ver una alerta.

@piecyk Probé tu solución con outerElementType y es muy lenta, ¿la has probado?: D

Lo he probado con react-scrollbars-custom . Es menos lento pero aún así. ¿Alguien ha tenido problemas con los retrasos al usar barras de desplazamiento personalizadas?

@ jancama2 no lo probó 😂¿Puedes compartir Code Sandbox cuando se vuelve lento?

@rufoot algo como esto https://codesandbox.io/s/4zjwwq98j4 ?

Estoy usando su implementación, pero me sale un error que dice que la referencia reenviada no es una función.

El problema en mi aplicación que estoy tratando de resolver es que tengo una gran lista de elementos y cuando hago clic en uno, la aplicación se redirigirá a otra ruta, por lo que necesito realizar un seguimiento de la posición de desplazamiento (probablemente en redux) y luego configure la posición de desplazamiento después del cambio de ruta.

@piecyk, ¿has probado a combinarlo con InfiniteLoader desde react-window-infinite-loader ?
Estoy usando react-scrollbars-custom y parece estar rompiendo el cargador infinito :(

Mi código:

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

Actualización: no creo que esté relacionado con el componente de desplazamiento infinito. No puedo hacer que funcione con la barra de desplazamiento personalizada.

Agradecería que alguien pudiera compartir conmigo la implementación de react-scrollbars-custom

@ranihorev , ¿necesitas usar ese paquete? El código pen @piecyk compartido anteriormente usa react-custom-scrollbars y funciona bien (excepto cuando se crea una referencia, que por alguna razón es nula en mi código)

Ok, necesito preguntar de nuevo. @piecyk Estoy usando su ejemplo de código de arriba (donde respondió a @simjes ). Sin embargo, en lugar de usar una lista de tamaño fijo, estoy usando el dimensionador automático para envolver un VariableSizeList y especificar el outerElementType. Algo como esto:
<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>

Implemento CustomScrollbars y CustomScrollbarsVirtualList de la misma manera que usted, sin embargo, la referencia siempre es nula. Intenté crear la referencia en el componente que estoy usando AutoSizer, así como en su padre, pero siempre es nulo. Realmente agradecería un poco de ayuda para entender por qué. Como mencioné anteriormente, necesito realizar un seguimiento de la posición de desplazamiento y poder configurarlo en un cambio de ruta en mi aplicación para que la lista no vuelva a la parte superior.

@ChristopherHButler Ya estoy usando ese paquete varias veces en mi código, así que prefiero seguir usando el mismo paquete. La solución es bastante similar, sin embargo, no obtiene nuevos datos ptoperly

@ChristopherHButler es realmente difícil de decir sin ningún ejemplo de Code Sandbox del problema, ¿puedes compartir algo?
Básico, la idea aquí es establecer la referencia de react-custom-scrollbars para que la ventana de reacción

AutoSizer o VariableSizeList funcionarán con este enfoque, como en este ejemplo
https://codesandbox.io/s/react-window-custom-scrollbars-t4352

@ranihorev ¿también puedes compartir el ejemplo de Code Sandbox?

@piecyk creo un ejemplo simple (y roto):
https://codesandbox.io/s/bvaughnreact-window-fixed-size-list-vertical-64kzh?fontsize=14

También probé otras variaciones (por ejemplo, estableciendo la referencia para que sea el envoltorio del desplazamiento) pero no tuve éxito ...

¡Gracias!

@ranihorev este enfoque sería el mismo independientemente de la implementación de desplazamiento personalizado. Necesitan establecer una referencia al mismo elemento que es responsable de desplazarse y manejar ese evento de desplazamiento.

react-scrollbars-custom proporciona una API más agradable usando el patrón de accesorios de renderizado para que pueda hacer algo como https://codesandbox.io/s/bvaughnreact-window-react-scrollbars-custom-pjyxs

No hice ningún perfil, espero que esto ayude 👍

@piecyk Estoy trabajando con tu demostración aquí: https://codesandbox.io/s/4zjwwq98j4 . Quiero usar onScrollStop para enviar una acción en redux para poder establecer la posición de desplazamiento (una idea de este hilo: https://github.com/malte-wessel/react-custom-scrollbars/issues/146 pero onScrollStart y onScrollStop no parece funcionar en mi código o en tu demostración. ¿Sabes por qué? ¿Hay alguna forma de pasar los accesorios de la Lista a las barras de desplazamiento?

@ChristopherHButler Creo que tu mejor opción es usar el contexto para pasar los accesorios, algo como

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

--- Editado
Si desmonta toda la lista cuando cambia la ruta, puede enviar la acción, entonces, Last scrollOffset se puede almacenar en ref y actualizar en cada cambio de desplazamiento usando onScroll: function de VariableSizeList, entonces no necesita pasar accesorios a Scrollbars

@piecyk eso es increíble, ¡gracias!
por cierto, parece que todo lo que necesita es la función onScroll (que me perdí) y no hay necesidad de reenviar una referencia en absoluto.
https://codesandbox.io/s/bvaughnreact-window-react-scrollbars-custom-99dn1

@piecyk lo siento, debería haber mencionado que mi implementación de VariableSizeList está envuelta en un AutoSizer y el componente que lo representa es un componente basado en clases, así que no estoy seguro de poder usar el contexto. ¿Quizás es por eso que no puedo hacer que onScrollStop funcione?

Actualización rápida:
Agregué el estado del componente y lo configuré en desplazamiento.

'
class BaseTree extiende Componente {
...
estado = {scrollPosition: 0,};
...
listRef = React.createRef ();
externalRef = React.createRef ();
...
render {
const {nodos, NodosFetching, NodosFetchingFailed, ExpandNodes, selectedNode} = this.props;
regreso (

{({alto, ancho}) => (
ref = {this.listRef}
className = "Lista"
externalRef = {this.outerRef}
externalElementType = {TreeScrollbar}
ancho = {ancho}
altura = {altura}
onScroll = {({scrollOffset}) => this.setState ({scrollPosition: scrollOffset})}
itemSize = {index => rowHeights [índice]}
itemCount = {nodeTree.length}
itemData = {{
nodeTree
extendedNodes,
nodos
nodesFetchingFailed,
selectedNode,
selectedNodeId,
resetRowHeight: this.resetRowHeight,
}}
>
{Nodo}

)}

);
}}

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

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

exportar conexión predeterminada (mapStateToProps, mapDispatchToProps) (BaseTree);
'

luego en componentWillUnmout despacho la acción para establecer la posición en redux así:

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

mi único problema ahora es poder establecer la posición de desplazamiento cuando el componente se vuelve a montar. Intenté acceder al método scrollTop en this.listRef así:

this.listRef.current.scrollTop() pero obtengo un error de que no es una función. No estoy seguro de qué propiedad (this.listRef o this.outerRef) puedo usar para establecer la posición de desplazamiento o en qué método. Estaba pensando que podría configurarlo en el componentDidMount de BaseTree de la siguiente manera:
componentDidMount() { const { scrollPosition } = this.props; if (this.listRef.current) { // console.log('initializing scroll position to : ', scrollPosition); this.listRef.current.scrollTop(scrollPosition); } }
¡Cualquier ayuda para que esto funcione será muy apreciada!
editar: Lo siento mucho, pero mi código no parece tener el formato correcto en este editor, así que me disculpo por eso :(

@ranihorev forward ref to react-window es necesario para que funciones como scrollTo, scrollToItem funcionen

El contexto de

El contexto proporciona una forma de pasar datos a través del árbol de componentes sin tener que pasar los accesorios manualmente en todos los niveles.

Pero sí, como se mencionó antes, si desmonta todo el componente en el cambio de ruta, no es necesario pasar los accesorios. No almacenaría scrollPosition en el estado ya que no lo necesita mientras se realiza el desplazamiento, menos re-renderizaciones, guárdelo en 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); 
}

Con respecto a outerRef , no es necesario en su caso. listRef.current.scrollTo es el método correcto que desea utilizar. Hmm, tu idea de llamar al método en componentDidMount es correcta y debería funcionar,

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

parece un problema de tiempo al configurar las referencias, tal vez AutoSizer retrasa el procesamiento de la Lista que hace que this.listRef.current no esté definido,

La opción hacky es romper el ciclo de vida de reacción con setTimeout y llamar a la referencia en el siguiente tick

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

@piecyk muchas gracias por la ayuda, realmente lo aprecio :)

Gracias @piecyk Eso parece funcionar. Hay un ligero parpadeo en el montaje después del cambio de ruta, pero puedo vivir con él :) También probé otro truco que consistía en usar className para pasar scrollPosition y establecer scrollTop en el refSetter de CustomScrollbars:
if (scrollbarsRef) { forwardedRef(scrollbarsRef.view); // HACK scrollbarsRef.scrollTop(className); }
Parece que trabaja adecuadamente.
Gracias de nuevo por toda su ayuda, se lo agradezco mucho !! 🙌🏻

Desafortunadamente, todos los ejemplos de este problema no funcionan correctamente (a veces el mouse deja de responder al desplazamiento).

¡Pero este ejemplo funciona perfecto!

@ranihorev este enfoque sería el mismo independientemente de la implementación de desplazamiento personalizado. Necesitan establecer una referencia al mismo elemento que es responsable de desplazarse y manejar ese evento de desplazamiento.

react-scrollbars-custom proporciona una API más agradable usando el patrón de accesorios de renderizado para que pueda hacer algo como https://codesandbox.io/s/bvaughnreact-window-react-scrollbars-custom-pjyxs

Lo he probado con react-scrollbars-custom . Es menos lento pero aún así. ¿Alguien ha tenido problemas con los retrasos al usar barras de desplazamiento personalizadas?

¿Lo lograste? Cual es la solucion final?

Si esto ayuda a alguien, he logrado que funcione con OverlayScrollbars . Solo necesita pasar el evento de desplazamiento al elemento correspondiente. Así es como:

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

Y luego, en su componente virtualizado:

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

Estoy usando esto con FixedSizeGrid pero debería funcionar igual para las listas.

Espero eso ayude.

¿Fue útil esta página
0 / 5 - 0 calificaciones