useDependentRef
Hook for defining Ref object that changes when deps is changed.
When any deps changed, new ref object will returned from hook but current value will be same.
This is useful when you fills your ref by condition and wants to rerun some effects.
Usage
import { useDependentRef } from '@krutoo/utils/react';
export function MyComponent({ someProp }) {
const ref = useDependentRef<HTMLDivElement>(null, [someProp, someGlobalVal]);
return <div ref={ref}>Hello!</div>;
}
Use cases
For example if you need to observe element by ResizeObserver you can write something like:
import { useEffect, useRef } from 'react';
function MyComponent({ visible }: { visible: boolean }) {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const element = ref.current;
if (!element) return;
const observer = new ResizeObserver(() => {
// do something on resize
});
observer.observe(element);
return () => observer.unobserve(element);
}, []);
if (!visible) {
return null;
}
return <div ref={ref}>My component</div>;
}
In this example, if you first render the component with a visible={false} and then re-render with visible={false}, your observer will not work.
This is because effect on first gets null from ref.
Also it is wrong to place ref.current to effect deps like this:
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
// ...
}, [ref.current]);
// ...
You can add ref itself to deps and this is ok, but regular ref is stable and will not be changed between renders.
Here the useDependentRef hook will help us - you can pass deps that cause ref object update (but not ref.current value).
import { useDependentRef } from '@krutoo/utils/react';
function MyComponent({ visible }: { visible: boolean }) {
// 1) we uses useDependentRef instead useRef
const ref = useDependentRef<HTMLDivElement>(null, [visible]);
useEffect(() => {
const element = ref.current;
if (!element) return;
const observer = new ResizeObserver(() => {
// do something on resize
});
observer.observe(element);
return () => observer.unobserve(element);
}, [ref]); // 2) we should add ref to other hook's deps
if (!visible) {
return null;
}
return <div ref={ref}>My component</div>;
}
And it will work as expected.
You may have noticed that in the last code example you could have simply added the visible prop
to effect dependencies and then it would have worked the same way.
But this approach would lead to the fact that you would have to add absolutely all variables that affect the display of your element to the effect dependencies.
It is more convenient to make the ref dependent on these parameters and add ref itself to the dependencies.