I'm not sure what the correct fix is to stop the following scenario. I've created this codesandbox to highlight the problem.
I have this hook and here is a scaled down version:
export const useAbortable = <T, R, N>(
fn: () => Generator<Promise<T>, R, N>,
options: Partial<UseAbortableOptions<N>> = {}
) => {
const resolvedOptions = {
...DefaultAbortableOptions,
...options
} as UseAbortableOptions<N>;
const { initialData, onAbort } = resolvedOptions;
const initialState = initialStateCreator<N>(initialData);
const abortController = useRef<AbortController>(new AbortController());
const counter = useRef(0);
const [state, dispatch] = useReducer(reducer, initialState);
const runnable = useMemo(
() =>
makeRunnable({
fn,
options: { ...resolvedOptions, controller: abortController.current }
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[counter.current]
);
const runner = useCallback(
(...args: UnknownArgs) => {
console.log(counter.current);
dispatch(loading);
runnable(...args)
.then(result => {
dispatch(success<N>(result));
})
.finally(() => {
console.log("heree");
counter.current++;
});
},
[runnable]
);
return runner;
};
The hook takes a function and options object and as they are recreated on each render, and the hooks use Object.is comparison, it was creating a new version of the returned function no matter what I do.
So I have hacked it like this, to use a counter:
const runnable = useMemo(
() =>
makeRunnable({
fn,
options: { ...resolvedOptions, controller: abortController.current }
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[counter.current]
);
I have had to silence the linter to make this possible.
What the linter suggests is this:
const runnable = useMemo(
() => makeRunnable({ fn, options: { ...resolvedOptions, controller: abortController.current } }),
[fn, resolvedOptions],
);
But fn and resolvedOptions are causing a new runnable function to be created each time.
It is a real pain having to wrap everything in useCallback, useMemo, and friends.
I've had a look at other fetch libraries and they are doing other things like this, like JSON.stringify dependency arrays to get around this problem.
I like hooks but the Object.is equality check is killing the whole paradigm.
What would be the correct way for me to use the dependency array correctly so I don't get a new function each time and also keep the linter happy? The 2 requirements seem to add odds with each other.