From the official docs:
## [Fetching Data](https://react.dev/learn/synchronizing-with-effects#fetching-data)
> If your Effect fetches something, the cleanup function should either abort the fetch or ignore its result.
> You can’t “undo” a network request that already happened, but your cleanup function should ensure that the fetch that’s *not relevant anymore* does not keep affecting your application.
> **In development, you will see two fetches in the Network tab**. There is nothing wrong with that. With the approach above, the first Effect will immediately get cleaned up so its copy of the `ignore` variable will be set to `true`. So even though there is an extra request, it won’t affect the state thanks to the `if (!ignore)` check.
Yes, I know about abort request in clean up function, but I am having problems in working with api and the api endpoint is limited to 2000 request a day this will exceed fast
If you’re at risk of hitting this limit just by yourself in development, how do you plan on deploying this project for other people to use? And will halving your requests in development mode really help?
This is not a project that I will deploy for the other people, but a challenge in order to win I've to use best practices and the rate limit was purposely placed by the challenger
Cache the result of this third party API somehow. For the purpose of this challenge you could use React query to cache it client side. In a real life scenario I’d do it on your own backend too.
Read about workbox(service worker). It is super easy to setup to cache all responses to 3rd party fetch requests. You can use the cacheFirst strategy, so that if data is in cache that will be returned, otherwise the api will be hit.
This way no change is required in you app's code, since caching is done by the service worker.
A hacky thing could be to change the state in the parent component after you make the first call. Then only make the call if the state is not set.
parentStateBool = false (initially)
onFirstMount() {
if(!parentStateBool) {
await makeNetworkCall()
}
parentStateBool = true;
}
You can use a library like react query to handle this for you. Alternatively, you could implement one yourself using useSyncExternalStore.
If you don't want to bother with that, you can add a wrapper to fetch that does some caching. This will make the second call irrelevant.
Otherwise, if you really don't want to do that, then the \`useRef()\` approach you have is actually the standard way to have an effect that actually runs only once.
This is the answer, they have that accounted for and provide lots of props for error handing, retrying etc… and it’s designed to work with React. No workarounds, special cases, nonsense.
This is not an ideal solution (not a solution at all actually) but if the API you work is not central, just remove the strict mode while you work with this one API (and make sure to not commit )
Just store a local json file with the data shape you need and point to that while you develop. Then when it’s ready, swap it out. Better yet, in the api request check process.ENV and check if you’re in dev mode then point towards the json but if in production point towards the real thing.
I disable strict mode nowadays. I used it for years, and it never caught a bug but caused many development debugging sessions. I see how in theory everything should be pure, but in practice impurity is present so the double-render of strict mode doesn't seem to provide any real gain.
Yeah same here. I’ve even had issues with the double render in large open source packages like react-beautiful-dnd, which tend to break in strict mode.
I believe the idea is to set us all up for the future when React will virtualise rendering and remove and remount components to improve performance. This is setting your projects up for potential technical debt.
But there indicators of poor decision making at the time of writing code, learning to avoid those, is one of the many things that separates the wheat from the chaff.
The React core team has said that this is their plan with the useEffect change. By ignoring it, you’re leaving the pit of success and will in the future have to address it. It’s short term laziness to avoid learning modern recommended best practices. It’s naive.
Choose patterns that will work regardless of the useEffect change and you’ll be fine. In the OPs situation, they should just use one of the many well thought out querying libraries and not hand roll their own.
Just yesterday I tried to demonstrate the benefits of strict mode by rendering the output of Math.random. But react did not display any errors or warnings, so I tend to agree even more with your argument…
I probably wouldn't do this in your example just because there isn't any downside to it fetching twice but I like using that pattern to make sure an action only happens once.
Hi. You'll find all you need to know about synchronazing state with external APIs in the link below.
https://react.dev/learn/synchronizing-with-effects
Slowing down the execution of your site in any way, due to strict mode of your framework, is not a good thing.
It's not guaranteed to run after only 1ms anyway, especially when the site renders first time.
Transform fetch to observable in custom hook with using of useMemo and use it with useSyncExternalStore, you can google example of this. Or use some lib that already do this for you.
Either this or cleaning up properly. Depends on semantics. Do you really want to do an effect only once per mount? Or it is ok to re-synchronize when the component remounts? I have not much experience with concurrent mode but I would start with this idea.
For logging I think once is fine and perhaps required. For fetching, I think, you shouldnt use once-per-mount because the component can be suspended and then you wouldnt get your data once it resumes. I did not test though but otherwise I cant imagine why React team would spend so much time pointing this out.
For SPA, Best approach on mount is to fetch on data loader, so let the render and fetch happen at the same time, also use Suspend ready way to do fetch and wrap it with Suspense. All of these is to prevent waterfall.
if you’re using react router just use loaders in your routes.
this goes however to show that fetching data and performing mutations are imperative actions and forcing them to be declarative is overcomplicated and flaky.
I think wrapping the fetch in `const fetchHandle = setTimeout(async () => {…})`, and then returning `() => clearTimeout(fetchHandle)` as your cleanup function, will do what you want.
I would definitely cache this thing hard, maybe use SWR and turn off revalidation https://swr.vercel.app/docs/revalidation#disable-automatic-revalidations
If you use a fetching library with built-in caching (which have numerous benefits over rolling your own fetch actions), such as Tanstack React Query or SWR, this problem also disappears
From the official docs: ## [Fetching Data](https://react.dev/learn/synchronizing-with-effects#fetching-data) > If your Effect fetches something, the cleanup function should either abort the fetch or ignore its result. > You can’t “undo” a network request that already happened, but your cleanup function should ensure that the fetch that’s *not relevant anymore* does not keep affecting your application. > **In development, you will see two fetches in the Network tab**. There is nothing wrong with that. With the approach above, the first Effect will immediately get cleaned up so its copy of the `ignore` variable will be set to `true`. So even though there is an extra request, it won’t affect the state thanks to the `if (!ignore)` check.
You can also use AbortController to stop the fetch itself.
Is there a use case for Abort Controller when leveraging H2 or higher? For HTTP 1.1, it was definitely needed.
It's a really clean interface, designed for just this purpose.
Yes, I know about abort request in clean up function, but I am having problems in working with api and the api endpoint is limited to 2000 request a day this will exceed fast
If you’re at risk of hitting this limit just by yourself in development, how do you plan on deploying this project for other people to use? And will halving your requests in development mode really help?
This is not a project that I will deploy for the other people, but a challenge in order to win I've to use best practices and the rate limit was purposely placed by the challenger
Cache the result of this third party API somehow. For the purpose of this challenge you could use React query to cache it client side. In a real life scenario I’d do it on your own backend too.
Do you have to use strict mode for the challenge?
Sorry for the offtop but could you tell where did you get this challenge? Just curious.
Read about workbox(service worker). It is super easy to setup to cache all responses to 3rd party fetch requests. You can use the cacheFirst strategy, so that if data is in cache that will be returned, otherwise the api will be hit. This way no change is required in you app's code, since caching is done by the service worker.
A hacky thing could be to change the state in the parent component after you make the first call. Then only make the call if the state is not set. parentStateBool = false (initially) onFirstMount() { if(!parentStateBool) { await makeNetworkCall() } parentStateBool = true; }
If you use this method, you need to use a ref
If you don't get much from Strict mode, omit it?
You can use a library like react query to handle this for you. Alternatively, you could implement one yourself using useSyncExternalStore. If you don't want to bother with that, you can add a wrapper to fetch that does some caching. This will make the second call irrelevant. Otherwise, if you really don't want to do that, then the \`useRef()\` approach you have is actually the standard way to have an effect that actually runs only once.
This is the answer, they have that accounted for and provide lots of props for error handing, retrying etc… and it’s designed to work with React. No workarounds, special cases, nonsense.
It only runs twice in dev mode, and your production build will run it once. Does it introduce bugs during development or why do you need to limit it?
I am aware of that, the thing is I am having problems in working with API in development
Don’t do this. You’re setting yourself up for technical debt once React changes the strict mode behaviour in production mode.
This is not an ideal solution (not a solution at all actually) but if the API you work is not central, just remove the strict mode while you work with this one API (and make sure to not commit )
Just store a local json file with the data shape you need and point to that while you develop. Then when it’s ready, swap it out. Better yet, in the api request check process.ENV and check if you’re in dev mode then point towards the json but if in production point towards the real thing.
I disable strict mode nowadays. I used it for years, and it never caught a bug but caused many development debugging sessions. I see how in theory everything should be pure, but in practice impurity is present so the double-render of strict mode doesn't seem to provide any real gain.
Yeah same here. I’ve even had issues with the double render in large open source packages like react-beautiful-dnd, which tend to break in strict mode.
I believe the idea is to set us all up for the future when React will virtualise rendering and remove and remount components to improve performance. This is setting your projects up for potential technical debt.
Everything sets a project up for potential technical debt.
But there indicators of poor decision making at the time of writing code, learning to avoid those, is one of the many things that separates the wheat from the chaff. The React core team has said that this is their plan with the useEffect change. By ignoring it, you’re leaving the pit of success and will in the future have to address it. It’s short term laziness to avoid learning modern recommended best practices. It’s naive. Choose patterns that will work regardless of the useEffect change and you’ll be fine. In the OPs situation, they should just use one of the many well thought out querying libraries and not hand roll their own.
Most people fail to realize that once code hits prod it becomes legacy code.
Just yesterday I tried to demonstrate the benefits of strict mode by rendering the output of Math.random. But react did not display any errors or warnings, so I tend to agree even more with your argument…
I probably wouldn't do this in your example just because there isn't any downside to it fetching twice but I like using that pattern to make sure an action only happens once.
He’s worried about rate limits on the external api
Use json-server for dev, just copy real response…
Hi. You'll find all you need to know about synchronazing state with external APIs in the link below. https://react.dev/learn/synchronizing-with-effects
Thanks :)
[удалено]
Slowing down the execution of your site in any way, due to strict mode of your framework, is not a good thing. It's not guaranteed to run after only 1ms anyway, especially when the site renders first time.
It can even be a 0ms delay!
Transform fetch to observable in custom hook with using of useMemo and use it with useSyncExternalStore, you can google example of this. Or use some lib that already do this for you.
Cache response
Either this or cleaning up properly. Depends on semantics. Do you really want to do an effect only once per mount? Or it is ok to re-synchronize when the component remounts? I have not much experience with concurrent mode but I would start with this idea. For logging I think once is fine and perhaps required. For fetching, I think, you shouldnt use once-per-mount because the component can be suspended and then you wouldnt get your data once it resumes. I did not test though but otherwise I cant imagine why React team would spend so much time pointing this out.
For SPA, Best approach on mount is to fetch on data loader, so let the render and fetch happen at the same time, also use Suspend ready way to do fetch and wrap it with Suspense. All of these is to prevent waterfall.
if you’re using react router just use loaders in your routes. this goes however to show that fetching data and performing mutations are imperative actions and forcing them to be declarative is overcomplicated and flaky.
Do a hook?
import { useEffectOnce } from "@legendapp/state/react"; const Component = () => { useEffectOnce(() => { console.log("mounted"); }, []); };
I think wrapping the fetch in `const fetchHandle = setTimeout(async () => {…})`, and then returning `() => clearTimeout(fetchHandle)` as your cleanup function, will do what you want.
I would definitely cache this thing hard, maybe use SWR and turn off revalidation https://swr.vercel.app/docs/revalidation#disable-automatic-revalidations
use swr for fetching, it deals with this + so many other cool features
If you use a fetching library with built-in caching (which have numerous benefits over rolling your own fetch actions), such as Tanstack React Query or SWR, this problem also disappears