Explicación del problema
Tengo una cuadrícula con un montón de mosaicos cuadrados (alrededor de 120). Si solo son visibles 8 mosaicos a la vez, entonces solo quiero renderizar unos 16 mosaicos.
Los 8 mosaicos visibles, más un búfer.Al desplazarse, cuando los 4 mosaicos superiores se desplazan fuera de la pantalla, estos mosaicos se reciclarán, se reemplazará la información que contienen con la información de los 4 mosaicos siguientes y luego se colocarán en el búfer para que se muestren. cuando el usuario se desplaza un poco más.
Esto realmente optimiza el componente si tienes muchos mosaicos, porque solo necesitas renderizar los que son visibles, más un búfer.
Referencia adicional
Este tipo de concepto se implementa en Flatlist de React Native, así como en Recycler View. He visto algunos módulos de nodo. wParece una versión reaccionar.js de una lista plana, pero no creo que la mayoría funcione, porque estoy tratando de implementar este concepto usando un diseño de tipo CSS Grid.
También he oído que este problema se describe como eliminación de cola
La mejor solución en línea
Después de observar muchos módulos de nodos, encontré un módulo de nodos de ejemplo de este concepto que utiliza un diseño de tipo cuadrícula llamado InfiniteScrollGrid, que tiene una demostración en vivo. El principal problema con este módulo de nodo es que es antiguo, no recibe mantenimiento, usa estilos antiguos de React, tiene vulnerabilidades de seguridad y ni siquiera pude compilarlo.
Otro problema es que me gustaría que los mosaicos individuales mantuvieran su relación ancho:alto original a medida que el usuario cambia el ancho de su navegador web
Solución intentada
Serbajo He intentado implementar la funcionalidad del módulo de nodo yo mismo usando ganchos. Hice todo lo posible para replicarlo, pero siento que no he entendido completamente todo el código de ese módulo de nodo.
Hay un repositorio de codesandbox y github para el siguiente ejemplo.
Nota: Por alguna razón, codesandbox tiene algunos errores que no aparecen localmente a pesar de que bifurqué el sandbox desde Github.InfiniteScrollGrid.js
import React, {useState, useEffect, useRef, memo} from 'react';
import PropTypes from 'prop-types';
import Item from './Item';
//Inifinately scrolling grid, that only renders as many items as visible on the screen
//Based on React Native FlatList, and this Github Repo https://github.com/ggordan/react-infinite-grid
const InfiniteScrollGrid = memo(({
buffer = 10,
padding = 10,
entries = [],
height = 250,
width = 250,
wrapHeight,
lazyCallback,
renderRangeCallback
}) => {
if (!entries.length) return null;
const itemHeightConst = height + (2 * padding);
const itemWidthConst = width + (2 * padding);
const wrapper = useRef();
const grid = useRef();
const [initiatedLazyload, setInitiatedLazyload] = useState(false);
const [numEntries, setNumEntries] = useState(entries.length);
const [minHeight, setMinHeight] = useState(2);
const [minItemIndex, setMinItemIndex] = useState(0);
const [maxItemIndex, setMaxItemIndex] = useState(100);
const [wrapperHeight, setWrapperHeight] = useState(wrapHeight);
const [itemHeight, setItemHeight] = useState(itemHeightConst);
const [itemWidth, setItemWidth] = useState(itemHeightConst);
const [itemGridWidth, setItemGridWidth] = useState(0);
const [itemsPerRow, setItemsPerRow] = useState(2);
const [initiatedLazyLoad, setInitiatedLazyLoad] = useState(false);
const gridHeight = Math.floor(entries.length / itemsPerRow) * itemHeight;
const getItemsPerRow = Math.floor(grid?.current?.clientWidth / itemWidthConst);
const numVisibleRows = Math.ceil(wrapper?.current?.height / itemHeight);
const scrolledPastRows = Math.floor((grid?.current?.height - grid?.current?.bottom) / itemHeight);
const [scrollOffset, setScrollOffset] = useState('');
const styles = {
wrapper: {
maxHeight: grid?.current?.clientHeight,
overflowY: 'scroll',
width: '100%',
height: wrapperHeight,
WebkitOverflowScrolling: true
},
grid: {
position: 'relative',
marginTop: padding,
marginLeft: padding,
minHeight: grid?.current?.clientHeight
}
};
const totalRows = () => {
const scrolledPastHeight = (entries.length / itemsPerRow) * itemHeightConst;
return scrolledPastHeight < 0 ? 0 : scrolledPastHeight;
};
const visibleIndexes = () => {
// The number of rows that the user has scrolled past
let scrolledPast = Math.max((scrolledPastRows * itemsPerRow), 0);
let min = Math.max((scrolledPast - itemsPerRow), 0);
let bufferRows = numVisibleRows + buffer;
let max = min(scrolledPast + (itemsPerRow * bufferRows), entries.length);
setMinItemIndex(min);
setMaxItemIndex(max);
};
const updateItemDimensions = () => {
// setItemHeight(itemHeightConst);
// setItemWidth(itemWidthConst);
setItemGridWidth(grid?.current?.width);
setItemsPerRow(getItemsPerRow());
setMinHeight(totalRows());
};
const scrollListener = (event) => {
clearTimeout(scrollOffset);
setScrollOffset(setTimeout(() => visibleIndexes(), 10));
};
useEffect(() => {
const resizeListener = () => {
if (!wrapperHeight) setWrapperHeight(window?.innerHeight);
updateItemDimensions();
visibleIndexes();
};
window.addEventListener('resize', resizeListener);
updateItemDimensions();
visibleIndexes();
return window.removeEventListener('resize', resizeListener);
}, []);
useEffect(() => { //TODO: May need to disable on the first render
if (!initiatedLazyload && (maxItemIndex === entries.length) && lazyCallback) {
setInitiatedLazyLoad(true);
lazyCallback(maxItemIndex);
};
}, [minItemIndex, maxItemIndex]);
useEffect(() => {
if (entries.length > numEntries) {
setInitiatedLazyLoad(false);
setNumEntries(entries.length);
};
visibleIndexes();
}, [entries]);
useEffect(() => {
if (typeof renderRangeCallback === 'function') {
renderRangeCallback(minItemIndex, maxItemIndex); //TODO: What is this about
};
}, [buffer, padding, entries, height, width, wrapHeight, lazyCallback, renderRangeCallback, initiatedLazyload, numEntries, minHeight, minItemIndex, maxItemIndex, wrapperHeight, itemHeight, itemWidth, itemGridWidth, itemsPerRow, initiatedLazyLoad]); //What are prev props and prev state
return (
<div
ref={wrapper}
className='infinite-grid-wrapper'
onScroll={scrollListener}
style={styles.wrapper}
>
<div ref={grid} className='infinite-grid' style={styles.grid}>
{entries.slice(minItemIndex, maxItemIndex).map((entry, i) => (
<Item
key={`item-${i}`}
index={minItemIndex + i}
padding={padding}
width={itemWidth}
height={itemHeight}
itemsPerRow={itemsPerRow}
data={entry}
/>
))}
</div>
</div>
);
});
InfiniteScrollGrid.propTypes = {
buffer: PropTypes.number,
padding: PropTypes.number,
entries: PropTypes.number,
height: PropTypes.number,
width: PropTypes.number,
wrapHeight: PropTypes.number,
lazyCallback: PropTypes.func,
renderRangeCallback: PropTypes.func
};
export default InfiniteScrollGrid;
Artículo.js
import React, {memo} from 'react';
import PropTypes from 'prop-types';
const Item = memo(({width, itemsPerRow, index, height, padding, data}) => {
const style = {
width: (width / itemsPerRow) - padding,
height: height - padding,
left: (index % itemsPerRow) * (width / itemsPerRow),
top: Math.floor((index / itemsPerRow) * height),
position: 'absolute'
};
return (
<div style={style} className="item">
<div>{data}</div>
</div>
);
});
Item.propTypes = {
width: PropTypes.number,
itemsPerRow: PropTypes.number,
index: PropTypes.number,
height: PropTypes.number,
padding: PropTypes.number,
data: PropTypes.any
};
export default Item;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Ejemplo de uso
import React from 'react';
import InfiniteScrollGrid from './index';
const ExampleItem = ({index}) => (
<div className='example'>
This is {index}
</div>
);
export default () => {
let items = [];
for (let i = 0; i <= 1000; i++) {
items.push(<ExampleItem index={i} />);
};
const lazyCallback = (index) => {
console.log(index);
};
return (
<InfiniteScrollGrid entries={items} wrapperHeight={400} lazyCallback={lazyCallback} />
);
};
Me gustaría una solución funcional para este componente de cuadrícula de desplazamiento infinito
1
Obtendrás la opción 'useEffect condicionalmente' error porque tienes if (!entries.length) return null; al comienzo de InfiniteScrollGrid. Debe asegurarse de que no haya declaraciones de devolución antes de realizar cualquier llamada a useX en su componente de función para evitar el error.
- Ben Clayton26/03/2021 a las 20:08
@BenClayton ¡Gracias! Actualicé el sandbox y github para arreglar esa parte
- Sam26/03/2021 a las 20:19
1
¿Probaste el paquete reaccionar-window? github.com/bvaughn/react-window
– deckele29/03/2021 a las 21:12
¿Aceptarías una respuesta con paquetes como reaccionar-ventana?
- 0piedra01 de abril de 2021 a las 17:15
@0stone0 Siempre aceptaré la mejor respuesta disponible, o al menos le daré la recompensa
- Sam1 de abril de 2021 a las 17:20
Le gustaría considerar la virtualización. reaccionar-virtualizado y reaccionar-ventana son opciones bastante populares
¿Quélo que desea a menudo se denomina ventana.
Recomendaría la ventana de reacción creada por el ingeniero principal de React, Brian Vaughn.
Aquí hay un ejemplo donde lo está usando.
No creo que el método "react-virtualizado" o algo similar te ayudará. Estás en una aplicación y si quieres procesar muchos datos, primero debes cargarlos. Cargar grandes cantidades de datos y luego mostrarlos en una lista virtual no es un escenario real. Lo que necesitas es una FlatList extendida. La FlatList normal tiene un "umbral" valor y un valor "onEndReached" método, que essuficiente para ampliar la lista. La lista se llena de vez en cuando y la aplicación es más lenta, por lo que necesita un "umbral" valor y un valor "onStartReached" método. El "umbral" El valor se seleccionará según el tiempo de carga y el valor "alcanzado". El método carga una cantidad razonable de datos. Como no está solo con este problema, ya existen implementaciones terminadas que puede utilizar como guía. Digo como guía porque todos cocinamos solo con agua. Busqué "desplazamiento infinito bidireccional" En google y lo encontré. Recomiendo encarecidamente utilizar los componentes estándar de reaccionar nativo y ampliarlos.
¡Feliz programación!
1
Estoy hablando de reacción regular, no de reacción nativa
- Sam2 de abril de 2021 a las 20:34