Hacker News new | ask | show | jobs
by purplerabbit 1552 days ago
Identity is necessary if you want to predictably trigger `useEffect` callbacks.

Example: if you're not using a smart intermediate layer like `react-query`, you can unintentionally trigger loading states and re-fetches if you're not closely watching dependency array identities

2 comments

Though this is also a strong reminder that though useEffect can entirely replace service layers and state management layers such as react-query/Redux/Mobx/Relay/what-have-you doesn't mean it necessarily should. (Ultimately that's the bottom line summary from this article.) useEffect is a very "raw" low-level tool, at some point it is a good idea to consider a higher-level tool (maybe one based on useEffect under the hood).

Don't forget too that trying to do everything in raw useEffect code may be a sign of putting too much business logic in your views and abstracting that out can be a good separation of concerns no matter how you decide to abstract that service layer (and/or which tools like react-query/Redux/Mobx/etc you choose to make that easier).

I can understand re-fetches can occur, that would be a performance rather than correctness issue though.

Nothing stopping you from keeping the prior response while loading the new one to handle loading states.

Disclaimer: I am working on a project which does not use hooks (or React or anything like that), but has a fairly complex set of data processing specifications. The project is > 10 years old, the project is a product in the sense that it has direct end users, but a library/framework in the sense that its behavior is also defined by end users (it’s not a UI around arbitrary spreadsheets, but that’s a pretty good common frame of reference). Most of these questions are informed directly by work I’m actively doing, some by past work on distributed systems and UI/UX.

- What about anything computed from the previously fetched data? Will it be computed the same way?

- What about any user-provided state downstream? Will it be preserved? Will it still be valid?

- What about any user-provided state midstream? Even if preserved, will it evaluate the same way after a refetch?

- If you know mid-/downstream user input might be impacted, can you detect that and ensure each case has a desirable outcome, or does this responsibility spread to all of those cases?

- What about inconsistent network connectivity? Will it fall back to the previous state in case of timeouts? Is it even supposed to? (Is the request idempotent? Do you know? Can you know? If it’s not idempotent, will it recover after a timeout once network available resumes?)

- What happens if user/event/timer-caused state changes while the request is in progress? How will computations be reconciled?

- What happens if network-provided data is also supplied by user input from other users? Do you have a reconciliation strategy?

- What happens if this first request triggers N requests? What happens if each of those N requests similarly has to answer all of the above questions?

- What happens if any one of these has a pathological case which causes it to cycle? What if it causes a cycle intermittently?

- What if your user is using the cheapest mobile available and has an expensive data plan?

- What if everything is really fast, actually, and your user has motion sensitivity?

I’m just rattling instinctive thoughts after stumbling on this comment. There are surely more I could come up with if I were actually dealing with concrete problems where unexpected redundant network requests are being evaluated as “is it more than a performance issue?”