@Boris007

Как правильно хранить данные и создавать новые блоки без перерисовки всего компонента при скролле?

При загрузке страницы в useEffect идет запрос для получения первых 10 продуктов
При скролле вниз, за 300px до конца происходит вызов функции async getProducts(query), который мы изначально запускали в useEffect
Как я понимаю, все предыдущие данные всегда нужно сохранять в useRef, но при обновления стейта при получении новых, у нас все равно обновляется весь компонент списка, а не добавляется еще 10 записей.
Как в реакте правильно реализовывается бесконечный скролл с добавлением блоков?
  • Вопрос задан
  • 64 просмотра
Решения вопроса 2
liaFcipE
@liaFcipE
В общем виде, я бы делал как-то так:

const LIMIT = 10;

const getProducts = async (offset, limit) => { ... }

function App () {
  const [offset, setOffset] = useState(0);
  const [products, setProducts] = useState([])

  useEffect(() => {
    getProducts(offset, LIMIT).then(products => setProducts(v => [...v, ...products]))
  }, [offset]);

  return <RenderProducts onScroll={() => { // increment offset }} />
}


Конечно, ваш бекенд должен уметь отдавать данные по limit & offset.
И лучше вынести всю работу с данными в стор, тогда не нужен будет уродливый useEffect.

Что-то вроде (nanostores):

// store.ts
const LIMIT = 30;

export const $products = map({
  offset: 0,
  products: [],
  isLoading: true
})

onMount($products, () => {
  fetchProducts()
})

const fetchProducts = action($products, "fetchProducts", async store => {
  const { products, offset } = store.get();

  // TODO: add error handling
  store.setKey("isLoading", true);
  const newPartOfProducts = await api.getProducts(offset, LIMIT);
  store.setKey("products", [...products, ...newPartOfProducts ]);
  store.setKey("isLoading", false);
});

const incrementOffset = action($products, "incrementOffset", store => {
  const { offset } = store.get();
  store.setKey("offset", offset + LIMIT);
  fetchProducts()
})

export const $productsMutations = { incrementOffset }

// App.tsx

function App () {
  const { isLoading, products } = useStore($products)

  return (
    <Fragment>
      <RenderProducts onScroll={$productsMutations.incrementOffset} products={products} />
      {isLoading && <LoadingIndicator />}
    </Fragment>
  )
}
Ответ написан
miraage
@miraage
Старый прогер
setState(prevItem => [...prevItems, ...newItems]) 
<ItemComponent key={item.id}>...</ItemComponent>
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы