Reputation: 19622
I have created a custom hook to scroll the element back into view when the component is scrolled.
export const useComponentIntoView = () => {
const ref = useRef();
const {current} = ref;
if (current) {
window.scrollTo(0, current.offsetTop );
}
return ref;
}
Now i am making use of this in a functional component like
<div ref={useComponentIntoView()}>
So for the first time the current always comes null, i understand that the component is still not mounted so the value is null . but what can we do to get this values always in my custom hook as only for the first navigation the component scroll doesn't work . Is there any work around to this problem .
Upvotes: 4
Views: 17806
Reputation: 9662
We need to read the ref
from useEffect
, when it has already been assigned. To call it only on mount, we pass an empty array of dependencies:
const MyComponent = props => {
const ref = useRef(null);
useEffect(() => {
if (ref.current) {
window.scrollTo(0, ref.current.offsetTop);
}
}, []);
return <div ref={ref} />;
};
In order to have this functionality out of the component, in its own Hook, we can do it this way:
const useComponentIntoView = () => {
const ref = useRef(null);
useEffect(() => {
if (ref.current) {
window.scrollTo(0, ref.current.offsetTop);
}
}, []);
return ref;
};
const MyComponent = props => {
const ref = useComponentIntoView();
return <div ref={ref} />;
};
We could also run the useEffect
hook after a certain change. In this case we would need to pass to its array of dependencies, a variable that belongs to a state. This variable can belong to the same Component or an ancestor one. For example:
const MyComponent = props => {
const [counter, setCounter] = useState(0);
const ref = useRef(null);
useEffect(() => {
if (ref.current) {
window.scrollTo(0, ref.current.offsetTop);
}
}, [counter]);
return (
<div ref={ref}>
<button onClick={() => setCounter(counter => counter + 1)}>
Click me
</button>
</div>
);
};
In the above example each time the button is clicked it updates the counter state. This update triggers a new render and, as the counter value changed since the last time useEffect
was called, it runs the useEffect
callback.
Upvotes: 9
Reputation: 2222
As you mention, ref.current
is null
until after the component is mounted. This is where you can use useEffect
- which will fire after the component is mounted, i.e.:
const useComponentIntoView = () => {
const ref = useRef();
useEffect(() => {
if (ref.current) {
window.scrollTo(0, ref.current.offsetTop );
}
});
return ref;
}
Upvotes: 2