Reputation: 153
I want to scroll to my previous window position by setting inside a useEffect the window position back to its previous state. To get the previous state, I am using useRef.
The Component was once class-based and there it worked perfectly. After I refactored it to hooks, this "shaky" behavior started.
Declaring the useRef
right at the beginning
const scrollRef = useRef(window.pageYOffset);
Whenever the component re-renders:
scrollRef.current = window.pageYOffset;
When the state gets updated:
useEffect(() => {
window.scrollTo(0, scrollRef.current)
});
The Complete Code:
export default () => {
const scrollRef = useRef(window.pageYOffset);
...
scrollRef.current = window.pageYOffset;
useEffect(() => {
window.scrollTo(0, scrollRef.current)
});
return (
...
);
}
On state update, I want to change back to the previous window position by not having this "shaky" behavior. (By shaky I mean it looks like he scrolls to the top and right after to the previous position so it looks like it shakes)
Upvotes: 5
Views: 10680
Reputation: 868
If I understand correctly, use useLayoutEffect
instead of useEffect
. This will scroll back to the top before the component is rendered. Remember to add the dependency array.
Upvotes: 1
Reputation: 302
The solution to your problem could look as follows:
First create a custom usePrevious
hook. This is just another function which uses the new useRef
method to store the previous scroll value and only updates it when a new value is passed to it.
// Hook
function usePrevious(value) {
// The ref object is a generic container whose current property is mutable ...
// ... and can hold any value, similar to an instance property on a class
const ref = useRef();
// Store current value in ref
useEffect(() => {
ref.current = value;
}, [value]); // Only re-run if value changes
// Return previous value (happens before update in useEffect above)
return ref.current;
}
In the actual component we declare a previousPosition
variable. Every time the component re-renders, the our customHook is executed with the current position. It returns the previous position which then gets assigned.
Also with each render, the useEffect
method is being executed, as we do not pass an Array as second argument. There we just compare the current scroll position with
the previous and scroll back to the previous in case it changed.
function Scroll(props){
const prevPosition = usePrevious(window.pageYOffset);
// Update scoll position with each update
useEffect(() => {
if(window.pageYOffset !== prevPosition){
window.scrollTo(0, prevPosition);
}
});
return (
<div>
...
</div>
);
}
Upvotes: 1