Code Monkey home page Code Monkey logo

virtual's Introduction

React Virtual Header

Headless UI for virtualizing scrollable elements in TS/JS and React

#TanStack semantic-release Join the discussion on Github

Enjoy this library? Try the entire TanStack! React Query, TanStack Table, React Charts

Visit tanstack.com/virtual for docs, guides, API and more!

Quick Features

  • Row, Column, and Grid virtualization
  • One single headless function
  • Fixed, variable and dynamic measurement modes
  • Imperative scrollTo control for offset, indices and alignment
  • Custom scrolling function support (eg. smooth scroll)

virtual's People

Contributors

aaastorga avatar adbayb avatar amitdahan avatar ardeora avatar avinashbot avatar beefchimi avatar benadam11 avatar casprwang avatar charkour avatar charlie632 avatar christiankaindl avatar dnicolson avatar fabervitale avatar gssakash avatar hampuskraft avatar ifndefdeadmau5 avatar karpengold avatar kouts avatar lachlancollins avatar meowtec avatar mogelbrod avatar o-alexandrov avatar pan-kot avatar perpetualwar avatar piecyk avatar tannerlinsley avatar tordans avatar yayvery avatar yeungkc avatar zhouzi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

virtual's Issues

changes to scrollOffsetFn ignored

I tried to implement something like:

  const rowVirtualizer = useVirtual({
    size,
    parentRef,
    estimateSize,
    scrollOffsetFn: () => {
      if (!parentRef.current) return 0;
      return Math.max(0, parentRef.current.scrollTop - offset);
    },
  });

and to my surprise it didn't work when offset was dynamic. It seems that initial scrollOffsetFn is used, and subsequent scrollOffsetFn are ignored.

I guess its as simple as needing scrollOffsetFn to be in the useMemo deps here:
https://github.com/tannerlinsley/react-virtual/blob/ea6b541a4e3d85d515c4d51af522643ec2be65c1/src/index.js#L100

Remove scarf-js

This package uses a package called scarf-js which collects analytics in a non-friendly way, the dependency should be removed, and all major versions using it should have patch bumps with the change.

final-form/react-final-form#822

Ambiguous API documentation

It seems like the term 'size' is used here as a kind of measurement(px) throughout the documentation,
but somehow only this required option is remained as an exception.


Options

  • size: Integer
    • Required
    • The size of the virtualizer

This could be easily misleading to understand this unit as a measurement too, which is wrong.
I think that instead of current description The size of the virtualizer, something like The total count of elements would be more appropriate.
Please correct me if I'm wrong

Rename `items` to `virtualRows`

What I am rendering I call "items" what you have given me is a "virtualRow". So rowVirtualizer.virtualRows seems to make more sense to me.

Cannot get correct scroll on last item

Hi! I picked up Kent's work on useCombobox and react-virtual and came up with this: https://codesandbox.io/s/epic-morse-jffe1.

Released an alpha for Downshift to fix some things, and now it should work, apart from this use case: highlighting from first item to last (using ArrowUp) is not bringing the item into view. Scroll somehow fails (unless I pass {align: 'start'} to the scroll function. However I don't want to do that for all cases.

Last item is scrolled correctly when moving from second to last to last (using ArrowDown).

Any idea why I get this bad scroll in the use case above? Thanks!

react-virtual testing

I migrated to react-virtual for a component using react-table and believe me after migration when i look at the code it now has much better DX.

One thing i want to check, has anyone tried to add tests to react-virtual, because it only renders the over-scanned rows in test.

@tannerlinsley i checked https://github.com/tannerlinsley/react-virtual/blob/master/src/tests/index.test.js, here also only overscanned rows are being tested it is not be able to render/test as expected.

Let me know if anyone has solution or am i missing something ?

Example with downshift has an issue on the first render

I'm trying to use react-virtual with downshift. In the downshift-examples there is an example with react-virtual:
https://codesandbox.io/s/github/kentcdodds/downshift-examples?file=/src/hooks/useCombobox/react-virtual.js

However, there is a huge issue with the first render of the menu. Their example doesn't show the issue because the menu's height is very small, and they have an overscan set to 2. When changing the overscan to 1 in their example you can see how only a few items are rendered on the initial render.

See the sandbox here where i set the overscan to 1:
https://codesandbox.io/s/suspicious-varahamihira-q42wn?file=/src/hooks/useSelect/react-virtual.js

Click on the [Elements] button. See how only 3 items are rendered? Once you start scrolling the others appear.

I expect this to be a bug in react-virtual that is why i reported the issue here.

Maybe this has to do with the use of a dummy li to specify the height?

How to NOT re-render list rows when scrolling?

Assume I have the following settings:

The container height: 400
The row height: 40

const virtual = useVirtual({
  size: 1000,
  parentRef,
  overscan: 20,
  estimateSize: useCallback(() => 40, [size]),
})

It seems that the container re-renders every visible row every time new row appears. This behavior maybe intentional but what if my row is heavyweight and the cost to re-render them is not trivial?

What I want to do is re-render only when the last row is visible.

In the above setup, I should see re-render happen once when i scroll to the 30th element, and insert next 20 element since overscan is 20.


Edit:

After play around with the source code, I see that setRange(prevRange => calculateRange(latestRef.current, prevRange)) actually cause the re-render every time index change.

I changed the if condition from

  if (!prevRange || prevRange.start !== start || prevRange.end !== end) {
    return {
      start: start,
      end: end
    };
  }

to


  if (!prevRange ||
    Math.abs(start - prevRange.start) > overscan - 1 ||
    Math.abs(end - prevRange.end) > overscan - 1
    ) {
    return {
      start: start,
      end: end
    };
  }

It reduce re-render from 999 to 50 when the data size is 1000 and overscan is 20.

Maybe we can allow developer to pass custom calculateRange function if they need to optimize performance? or improve calculateRange when overscan is used?

https://github.com/tannerlinsley/react-virtual/blob/master/src/index.js#L77

Re-measure on height change

Hi @tannerlinsley, is there a way to re-measure a row height when it changes? I have a virtualized list with truncated descriptions that can be expanded, and I tried to recreate a similar use case in the sandbox and it seems the only way to remeasure would be either scrolling or change estimateSize memoization.

Here's the example, when you click one of the rows, its height change and they start to overlap.
May-09-2020 10-33-34

Change in height of parent div, not re-renders complete view-port list

@tannerlinsley
Thanks for this great library,
I was upgrading react-window with react-virtual and encountered an issue

https://codesandbox.io/s/hopeful-bassi-fxbdk?file=/src/index.js

When I controlling the height of parent ref div based on item rendering, then dynamically changing the item count, then rerending is not happening properly.
Check on the above code sandbox link, click on the button then we to scroll a bit to refresh the list.

image
image

Dynamic height on Internet Explorer 11 causes infinite render

Hi,
I am trying to use react-virtual on Internet Explorer 11 to render a dynamic-height list. It looks like getBoundingClientRect returns high precision floating point numbers as element's height and these measurements somehow slightly change at each render. For example, an element's height is measured as 32.6201171875 at first and 32.619995117 at the second pass. That's why the list updates itself in infinite loop.

Initially I thought I can fix it by using .toFixed(2) at index.js:110 but cannot be sure if it breaks anything.

Fix Infinite Scroll Example Network Error

The InfiniteScroll Example in the sandbox fails with a "network error". You should be able to fix this by using https in the get:

const { data } = await axios.get(
  "https://jsonplaceholder.typicode.com/posts?_limit=10&_page=" + nextPage
);

Fixed Cols/Grid - Is there a way to determine the current scroll position?

I'm trying to use this library together with React-Table inside a Material-UI table.

The goal is to have a TableBody that is virtualizd in both horizontal & vertical directions and a TableHead that always stays on top and is scrolled horizontally 'with' the TableBody.

Here's a simple codesandbox that illustrates the use case.

Is there a way to get the current scroll position of the TableBody and trigger a scrollToOffset on the TableHead?

Maybe there's another solution to this case... Any piece of info will help.

recalculate method?

Hi, thank you so much for the great project.

I was wondering if there was a recalculate/refresh list method for the dynamic lists? It would be to support updating the height of the row once edited.

Resizing rows in dynamic list

I've given the dynamic list of this great lib a spin and encountered two issues:

  1. My rows can expand/collapse on click (i.e. change height), but the virtualized list doesn't adapt the item positions. Could this benefit from a ResizeObserver hook such as useMeasure?
  2. It would be very nice to have a mechanism where the dynamic list's overall initial height is set such that it doesn't cut off partial rows. See e.g. the first example shown in react-collapse where the height limit is set by the number of visible paragraphs.

TypeError: Cannot read property 'addEventListener' of undefined

const rowVirtualizer = useVirtual({ size: pokeData ? pokeData.pokemon_entries.length: 0, parentRef, estimateSize: useCallback(() => { return pokeData ? pokeData.pokemon_entries.length: 0 }, []);, overscan: 12 });
Screenshot 2020-09-26 at 8 30 46 AM

may I know what is the reason, I developed this in react typescript

I'm getting Maximum update depth exceeded when i'm using 100k in columns and rows in dynamic example

Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
▶ 4 stack frames were collapsed.
ref
/src/index.js:194:36
  191 | key={virtualColumn.index}
  192 | ref={el => {
  193 |   virtualRow.measureRef(el);
> 194 |   virtualColumn.measureRef(el);
      |                ^
  195 | }}
  196 | className={
  197 |   virtualColumn.index % 2

How is it different from react-window/react-virtualized?

Hi, @tannerlinsley! Thanks for the amazing work here!

As a maintainer, I know that this kind of question is a little bit annoying. But, as a user of alternative libraries like react-window, it feels like knowing how it compares to the available alternatives, beyond the hook API, is the easiest way to understand the library.

So, my suggestion is to add a section in the README about this.

Dynamic measurement not working

First of all thanks for great lib. I'm big fan of your OSS projects!

measureRef do noting in current implementation. If we looking into official example we can see that height of row came from hardcoded array value (${rows[virtualRow.index]}px) this behaviour isn't look like "dynamic measurement"

ReferenceError: options is not defined

Just trying the hook in the sandbox. In the smoothScroll example, clicking the "scroll to random index" triggers an error ReferenceError: options is not defined

Unnecessarily re-rendering when unchanged?

I modified one of the basic examples to log the items being rendered, and noticed that the component using the useVirtual hook gets re-rendered every time onscroll fires (once for every pixel scrolled unless debounced). This seems unnecessary and counter-intuitive - I expected the hook to only trigger renders when virtualItems (or any other returned values) changes. Is this expected behaviour, a bug, or oversight?

Modified example with logging on codesandbox

Other than this the hook looks really solid 👍

Edit: Looks like it's due to useScroll updating the state on every scroll + useRect() updating the state whenever getBoundingClientRect() returns a new value, which in turn happens on every scroll since the top and x properties are relative viewport. Since only height or width is being used the state updates should likely be limited to when these values change.

Type of estimateSize param

Currently:
estimateSize?: (index?: number) => number
should be
estimateSize?: (index: number) => number

rowVirtualizer.virtualItems gets with only one item on a chat application when an user send new message

I'am bulding a chat application using react-virtual to render the messages there are only on the window, but I'am getting a little problem, when an user send a new message on the chat the rowVirtualizer.virtualItems that I'am consoling print an array with only one item than after (at the same second) it prints the array with the new item. This results in a really weird behavior, because since my chat get no messages, it becomes blank, then after it gets the messages.

The console.log of the rowVirtualizer.virtualItems:

Captura de Tela 2021-02-19 às 13 35 24

Chat behavior:

chat

react-window

Do you plan something like react-window for this lib?
If not, then is it only for small lists or why is it good enough?

Usage with react-table?

I cloned the react-table's Simple sandbox to get react-virtual working with it.

But I'm confused what to put instead of rowVirtualizer.virtualItems when using .map() inside tbody?

Also, confused if I have to wrap the whole table or just tbody as it's the only thing that needs scrolling.

I think all the rows get rendered because the rendering is too slow for only 2000 items. I'm sure I must be doing something wrong.

Here's my CodeSandbox 👉 https://codesandbox.io/s/react-table-basic-with-react-virtual-tq047?file=/src/App.js

And here's the same code if I ever delete it:

import React from "react";
import styled from "styled-components";
import { useTable } from "react-table";
import { useVirtual } from "react-virtual";

import makeData from "./makeData";

const Styles = styled.div`
  padding: 1rem;

  table {
    border-spacing: 0;
    border: 1px solid black;

    tr {
      :last-child {
        td {
          border-bottom: 0;
        }
      }
    }

    th,
    td {
      margin: 0;
      padding: 0.5rem;
      border-bottom: 1px solid black;
      border-right: 1px solid black;

      :last-child {
        border-right: 0;
      }
    }
  }
`;

function Table({ columns, data }) {
  // Use the state and functions returned from useTable to build your UI
  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow
  } = useTable({
    columns,
    data
  });

  const parentRef = React.useRef();
  const rowVirtualizer = useVirtual({
    size: rows.length,
    parentRef,
    estimateSize: React.useCallback(i => rows[i], []),
    overscan: 5
  });

  // Render the UI for your table
  return (
    <div
      ref={parentRef}
      className="List"
      style={{
        height: `450px`,
        width: `650px`,
        overflow: "auto"
      }}
    >
      <div
        className="ListInner"
        style={{
          height: `${rowVirtualizer.totalSize}px`,
          width: "100%",
          position: "relative"
        }}
      >
        <table {...getTableProps()}>
          <thead>
            {headerGroups.map(headerGroup => (
              <tr {...headerGroup.getHeaderGroupProps()}>
                {headerGroup.headers.map(column => (
                  <th {...column.getHeaderProps()}>
                    {column.render("Header")}
                  </th>
                ))}
              </tr>
            ))}
          </thead>
          <tbody {...getTableBodyProps()}>
            {rows.map((row, i) => {
              prepareRow(row);
              return (
                <tr {...row.getRowProps()}>
                  {row.cells.map(cell => {
                    return (
                      <td {...cell.getCellProps()}>{cell.render("Cell")}</td>
                    );
                  })}
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    </div>
  );
}

function App() {
  const columns = React.useMemo(
    () => [
      {
        Header: "Name",
        columns: [
          {
            Header: "First Name",
            accessor: "firstName"
          },
          {
            Header: "Last Name",
            accessor: "lastName"
          }
        ]
      },
      {
        Header: "Info",
        columns: [
          {
            Header: "Age",
            accessor: "age"
          },
          {
            Header: "Visits",
            accessor: "visits"
          },
          {
            Header: "Status",
            accessor: "status"
          },
          {
            Header: "Profile Progress",
            accessor: "progress"
          }
        ]
      }
    ],
    []
  );

  const data = React.useMemo(() => makeData(2000), []);

  return (
    <Styles>
      <Table columns={columns} data={data} />
    </Styles>
  );
}

export default App;

measureRef gives wrong size at first render

Hi,
I am trying to use react-virtual to render a dynamic-height list. When I pass measureRef to my list item wrapper like <div ref={measureRef}><ListItem /></div> it calculates the height of each visible list item as 48px. But this value is not correct. Calculation becomes correct when I start scrolling for the first time to trigger a change on visible portion of the list.

When I pass the ref using a arrow function like <div ref={element => measureRef(element)}><ListItem /></div> it calculates and renders correctly.

Post install script is harmful

Since we installed this package, 50% of our CI/CD pipeline started failing.

The reason is that this package uses a post install script.

Here is the error which is thrown 50% of the time during yarn install:

# This file contains the result of Yarn building a package (react-virtual@virtual:02f6a86156759c9bb9f5bd96fa55ba1015b8993e779050fe9b358272baec2106973f2be4251a130e2a18a9511f3b6b721ac69018e06c8384da2a44074e767c50#npm:2.3.1)
# Script name: postinstall

UsageError: Couldn't find �[38;2;215;135;95mreact-virtual�[39m�[38;2;135;175;255m@�[39m�[38;2;135;175;255mnpm:2.3.1 [02f6a]�[39m in the currently installed PnP map - running an install might help
    at B.findPackageLocation (/Users/vincent/Code/sterblue3/.yarn/releases/yarn-2.3.3.cjs:2:251129)
    at F.A.A.openPromise (/Users/vincent/Code/sterblue3/.yarn/releases/yarn-2.3.3.cjs:2:401596)

This is caused by the post install script:

#!/usr/bin/env node

var msg = `
👩‍💻 Use react-virtual at work?
🎉 Consider sponsoring/supporting its development at:
  
  https://github.com/sponsors/tannerlinsley
`

console.log(msg)

Post install scripts should only be used in very specific cases, as explained here

opencollective/opencollective#1625

Also, there is now first class support for package support messages, to avoid this problem:

https://github.com/nodejs/package-maintenance/blob/main/docs/PACKAGE-SUPPORT.md

I will create a MR to remove the postinstall script

Totalsize, start and end properties are not created properly!

I've passed down an array of objects to useVirtual, each object in that array has the following shape:

allCells: (16) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
canExpand: false
cells: (15) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
depth: 0
getRowProps: ƒ (userProps)
getToggleRowExpandedProps: ƒ (userProps)
getToggleRowSelectedProps: ƒ (userProps)
id: "invoice_lines[0]"
index: 0
isExpanded: undefined
isSelected: false
isSomeSelected: false
original: {id: 46317, created_at: "2020-06-28T16:45:34.000000Z", created_by_id: 279, modified_at: "2020-08-20T07:28:26.000000Z", modified_by_id: 1, …}
originalSubRows: []
subRows: []
toggleRowExpanded: ƒ (set)
toggleRowSelected: ƒ (set)
values: {select: undefined, drag: undefined, product_id: 1907, name: "Consult Revisit", created_by_id: 279, …}
__proto__: Object

The hook returned a rather strange object, with some properties displaying an object string.

scrollToIndex: ƒ ()
scrollToOffset: ƒ (toOffset, _temp)
totalSize: "0[object Object]0"
virtualItems: Array(1)
0:
end: "0[object Object]"
index: 0
measureRef: ƒ measureRef(el)
size: {id: "invoice_lines[0]", original: {…}, index: 0, depth: 0, cells: Array(15), …}
start: 0
__proto__: Object
length: 1
__proto__: Array(0)
__proto__: Object

I could not make head nor tail of it. What's the underlying problem ?

virtualRow will contain one empty item when size is 0 (since v2.2.4)

Bug Report

I'm experiencing a bug, where when having an empty list (ie. size parameter in useVirtual is 0), the returned virtualRow array still contains a single element. That single element is "empty", as it is an object that only contains a measureRef key, and none of the other keys (index, start, end, size).

Reproduction

I've created a minimal reproduction of the issue at https://codesandbox.io/s/amazing-glade-eo7vw?file=/src/index.js which is a fork of the "Dynamic Rows" example. It starts off with having an empty list, and then after 5 seconds, changes the list to have items. The console log (and the error) shows that we start with an empty virtualRow, and then get correct virtualRows after 5 seconds.
What should happen instead, is that no console logs are present in the beginning, because the virtualRows array should be empty.

Context

I believe this bug was introduced in version 2.2.4 by #48. Downgrading to version 2.2.3 "fixes" the issue. But as the changes in that PR are rather substantial, I haven't been able to pinpoint exactly where the problem is. Pinging @mogelbrod

This might be the same bug as #49.

Workaround

Current workarounds are:

  1. downgrade to version 2.2.3
  2. before mapping through the virtualRows, ensure that you have at least one raw item (and NOT just one virtualRow), eg.
<div>
  {items.length > 0 && rowVirtualizer.virtualItems.map((virtualRow) => {
    ...return rows
  })}
</div>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.