Catlike
Catlike7mo ago

useUtils changes at every render and causes useEffect to be called continuously

I have a context provider where I need to invalidate a query on unmount. So I wrote this piece of code:
const utils = trpc.useUtils();
useEffect(() => (
() => utils.user.example.invalidate()
), [utils.user.example.invalidate]);
const utils = trpc.useUtils();
useEffect(() => (
() => utils.user.example.invalidate()
), [utils.user.example.invalidate]);
Howerver, the cleanup function is called continuously when other fields of the context provider changes even without involving trpc. I also tried to use as dependency utils, utils.user and utils.user.example. A workaround coul be to pass [] as dependencies and then make an exception on eslint, but it would be nice if useUtils would return the same instance instead of changing at every render.
2 Replies
jonathanj
jonathanj7mo ago
I'm not sure if it's documented somewhere, I might have read through the code trying to answer this, but I think your dependency should be the result of useUtils only. The result of useUtils is a Proxy object and the useUtils property is specially handled to memoize the context result. Accessing other properties on the proxy creates a new reference every time you access the property. e.g.
const utils = trpc.useUtils();
useEffect(() => () => utils.user.example.invalidate(), [utils]);
const utils = trpc.useUtils();
useEffect(() => () => utils.user.example.invalidate(), [utils]);
Catlike
CatlikeOP7mo ago
Thanks for the response! I'm pretty sure I tried to use "utils" as dependency but the problem persisted - I'll give a double check, maybe I missed something