React Hooks — keep scroll position during change list views
During working on one of my projects I met an interesting problem. Let’s say you have a list of items which can be shown as a simple grid of items or in the detailed grid as cards with images, description etc, like mocks bellow.
Implementation of this is really simple, the view switch state can be stored in useState hook or even in Redux if it’s needed.
So, where is a problem?
The problem which I met is when you scroll down and then change the view, your scroll position will remain as it was before the change and because of this, you will see different items then was in the previous view. For better understanding — imagine you scroll to Item 5 than you change the view to CARD/details, and you can’t see Item 5, but it will show Item 2 because card items are higher than list items. See example below:
How to solve it?
You can solve this in the following steps:
- Before switch check with the item is currently in the view
- Store this information
- Change view
- Check the position of stored item
- Change scroll position to the stored item
The main problem is how to call the function before changing view. You can, of course, listen to scroll event and saving item, but it doesn’t have good performance. The perfect way would be to check DOM directly before changing view and WITHOUT changing toggle function.
getSnapshotBeforeUpdate
For a situation like this react prepared lifecycle method called getSnapshotBeforeUpdate. You can read more about this here https://reactjs.org/docs/react-component.html#getsnapshotbeforeupdate
But our aim isn’t refactoring now lists to Class Component. So how about hooks?…
Using hooks - useMemo
After a long time searching how we can do this without class component I found information that useMemo is called before rendering component. Thanks to dependencies array in useMemo
you can call the function only when view state change. Combining this with useLayoutEffect
which is called AFTER render you can create something like this for pinning scroll to the element currently in view.