Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to force a rerender of useVirtualizer? #528

Closed
2 tasks done
ocodista opened this issue Mar 9, 2023 · 6 comments
Closed
2 tasks done

How to force a rerender of useVirtualizer? #528

ocodista opened this issue Mar 9, 2023 · 6 comments

Comments

@ocodista
Copy link

ocodista commented Mar 9, 2023

Describe the bug

I need to change the height of a list item when the user clicks on it.

It needs to be an InfiniteScroll with dynamic height.

I was able to update the height of the element clicked but the other ones (after them) are not re-rendered, causing them to stack.

I created a sandbox with the reproduceable bug, you only need to click in any of the items to see it.

https://codesandbox.io/p/sandbox/optimistic-edison-jebzqk?file=%2Fsrc%2Fmain.tsx&selection=%5B%7B%22endColumn%22%3A40%2C%22endLineNumber%22%3A72%2C%22startColumn%22%3A40%2C%22startLineNumber%22%3A72%7D%5D

image

image

Your minimal, reproducible example

https://codesandbox.io/p/sandbox/optimistic-edison-jebzqk?selection=%5B%7B%22endColumn%22%3A1%2C%22endLineNumber%22%3A109%2C%22startColumn%22%3A1%2C%22startLineNumber%22%3A109%7D%5D&file=%2Fsrc%2Fmain.tsx

Steps to reproduce

  1. Click on any row of the list

Expected behavior

  1. Expand the height of the clicked row
  2. Adjust the rows below it to prevent stacking

How often does this bug happen?

Every time

Screenshots or Videos

image

image

Platform

  • Browser

tanstack-virtual version

3.0.0-beta.48

TypeScript version

No response

Additional context

No response

Terms & Code of Conduct

  • I agree to follow this project's Code of Conduct
  • I understand that if my bug cannot be reliable reproduced in a debuggable environment, it will probably not be fixed and this issue may even be closed.
@piecyk
Copy link
Collaborator

piecyk commented Mar 10, 2023

Hi @ocodista to read height out of box using the resize observer please attach additional props on item, like

key={virtualRow.key}
data-index={virtualRow.index}
ref={rowVirtualizer.measureElement}

https://codesandbox.io/p/sandbox/dynamic-height-change-height-on-click-forked-gq33ld

@ocodista
Copy link
Author

Jesus, this is great, thank you!!!

@a5006
Copy link

a5006 commented Oct 30, 2023

Hi @ocodista to read height out of box using the resize observer please attach additional props on item, like

key={virtualRow.key}
data-index={virtualRow.index}
ref={rowVirtualizer.measureElement}

https://codesandbox.io/p/sandbox/dynamic-height-change-height-on-click-forked-gq33ld
Jesus, it's working! nice mate, you are the true hero!

@alexkatz
Copy link

alexkatz commented Nov 1, 2023

I'm having a similar issue, and in my case setting key={virtualRow.key} (rather than key={myRow.id}) fixes the occasional missed render, but breaks my animations on insert/remove rows.

I understand why, as changing the key prop on a React node essentially removes and re-adds it to the DOM.

However, for insert/remove animations to work, each row's key must be tied to a unique identifier in the underlying data, right? This is the only way for React to track that row N is now at, say, N+10 after adding 10 rows above it, so row N remains in the DOM and smoothly animates to a new position. I hope that makes sense.

Is there a way to trigger re-renders that doesn't involve resetting keys?

@alexkatz
Copy link

alexkatz commented Nov 2, 2023

I found a relatively straightforward solution to my above comment shortly after posting it -- listening to virtualItem.start and calling measureElement within my row component as so:

useEffect(() => {
  virtualizer.measureElement(ref.current);
}, [virtualItem.start, virtualizer]);

I can't notice or detect any degradation in performance, but who knows. As far as I can tell, this makes measureElement fire less often than if it were in the ref callback, and only causes re-renders that are likely necessary when virtualItem.start changes anyway.

@piecyk
Copy link
Collaborator

piecyk commented Nov 2, 2023

hmm if you don't use arrow it on ref, just pass measureElement then it should fire only once, https://react.dev/reference/react-dom/components/common#ref-callback

React will also call your ref callback whenever you pass a different ref callback. In the above example, (node) => { ... } is a different function on every render. When your component re-renders, the previous function will be called with null as the argument, and the next function will be called with the DOM node.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants