🔮 Sanity Create is here. Writing is reinvented. Try now, no developer setup

React component loses state when navigating away from browser tab

28 replies
Last updated: Sep 14, 2022
Perhaps this isn't Next-specific but a React thing, but I've tried searching for both on Google and couldn't arrive at an answer.
I have a single functional component which shows and hides "paginated" child components where the page is kept track of through useState.

If I am on a "page" (say, the third component) and navigate to another
browser tab and come back, it looks like the state resets (it defaults to 0, the first "page") and I lose my progress.
The navigation is just changing the state number to conditionally show and hide child components ( like if page = 1 && <show this>, etc.)

I'd rather not kick off to something like localStorage trying to persist it if there's a lower-key way to handle it, but perhaps I also need to approach the construction in a different way?
Sep 13, 2022, 4:26 PM
Can you move the state higher up?
Sep 13, 2022, 5:02 PM
I might not be understanding the problem but what about storing the page state in a param? eg.
and have your component read from that instead of internal state?
Sep 13, 2022, 5:02 PM
user F
It's the very first thing that happens in the page's default export; do I have to move it into the _app itself to preserve it? I guess I naively thought there might be something like "heydon'tchangethis" I could flag.
There isn't even a side effect, it's literally just got a default state and clicks change it back and forth.

user J
My experience with params has led me to believe the page could no longer be static if that were the case; if it can, would I just use the router instead of props to ascertain that?
(Thank you both for responding, by the way, and so quickly
😃 )
Sep 13, 2022, 5:21 PM
Yeah, if you use query params, you can no longer statically build the pages but you could rely on SSR.
Sep 13, 2022, 5:30 PM
Another approach is to use route params instead.
Sep 13, 2022, 5:30 PM
And statically build the pages.
Sep 13, 2022, 5:30 PM
Is there a technological reason it would do that in general? I don't remember other components losing state (like, say, entries into a form field) set to e.target.value onChange) but maybe I just wasn't paying attention.
Sep 13, 2022, 5:31 PM
If I am on a "page" (say, the third component) and navigate to another browser tab and come back, it looks like the state resets (it defaults to 0, the first "page") and I lose my progress.
This shouldn’t happen unless you have a browser extension that “shuts down” tabs.
Sep 13, 2022, 5:32 PM
It’s weird that navigating to a different browser tab and returning to another tab would be causing this tbh.
Do your tabs refresh’ when you visit them? Browser extensions? Or are you using something like SWR?
Sep 13, 2022, 5:34 PM
I just tested it with the pretend page with form fields like
const [page, setPage] = useState(2) instead of the default
0 and tried typing into them and then went to another browser tab and came back and they, too reset....
I believe in local testing that there are renders triggered, that's not new to me, but usually it's just a repaint (so like if I had a useEffect with a fetch, that might get called again)....but this just goes back to whatever the default is

Nothing is even being fetched, so now fetch and now SWR.

I have AdBlock Plus is all and have tried deactivating it for the page and removing it entirely, no difference

It's just

{ page == 0 && <OneThing> }
{ page == 1 && <TwoThing> } etc. (simplifying the names for clarity)
Sep 13, 2022, 5:38 PM
Have you tried opening tabs in incognito mode so no extensions should interfere with the page and see if it happens then?
Sep 13, 2022, 5:42 PM
Or another browser?
Sep 13, 2022, 5:45 PM
I have. I actually straight-up uninstalled it, and tried in Firefox and Edge.
I checked all my component keys as well to make sure I didn't typo them out of uniqueness.

I suspected it might be Framer Motion because of AnimatePresence and just the "illusion" of certain things appearing and disappearing but it doesn't bear out in the markup and also anything that's not in them that includes the state also resets.
Sep 13, 2022, 5:49 PM
That’s a good guess. What if you remove framer-motion from that page?
Sep 13, 2022, 5:51 PM
I am surprised to learn that that didn't make a difference
Sep 13, 2022, 5:54 PM
Any chance I could play with your code?
Sep 13, 2022, 5:54 PM
Under normal circumstances I'd be happy and relieved to but there's enough in there that would break my NDA. (I do appreciate how annoyingly that binds the open call for help)
Sep 13, 2022, 6:02 PM
Ah yes, I understand.
Sep 13, 2022, 6:04 PM
Normally I'd just explain the reset away if anyone asked but it's onboarding with like nine pages so you really don't want anyone losing that progress.
I will definitely let you both know here if I learn anything. I have tried scraping out any mentions of the state on the child components in case they "received" it enough to trigger something but no dice.
Sep 13, 2022, 6:07 PM
I would move the state into the URL, e.g.:
<base page>/page/2
, and use
<base page folder>/[page].js
to build the pages you need. Then you would have to make sure to update the URL using the browser's History API. You should also probably fetch that the updated page by listening for the update in URL.
Sep 13, 2022, 6:58 PM
You would generate a number of very similar pages, so the total size of your site would increase, but you would get the effect you want.
Sep 13, 2022, 6:59 PM
Yeah, that’s what I suggested earlier as well:
Another approach is to use route params instead.
I would agree with that approach. 🙂
Sep 13, 2022, 7:00 PM
Ah, yes, true, sorry for not recognizing that ^_^
Sep 13, 2022, 7:01 PM
No, no, no worries. All good. ❤️ I also didn’t expand much on it.
Sep 13, 2022, 7:01 PM
One of the reasons I kept them as child components (representing pages) on one real page is they all have forms, and the idea was to do a multi-page form where each legitimate submission fed back to the overarching state at the top of the (parent) file.
If they're separate real pages I believe I then have to move from unmounting children into page transitions and move from a single (albeit busy) state to a form context provider.

Still investigating, but in either case I continue to appreciate the extra eyes
Sep 13, 2022, 7:31 PM
OY! It's one of those forehead-slapping ones. And if I'd only been able to share the code I could have spared you both the sacrifice of time from my oversight.
In this onboarding, because it's an onboarding, you have to be logged in. I had a Spinner component from Sanity UI in place so there was something to indicate, and it was a previous state from the auth library ( i.e
{ isLoading, user, error } ) in one of those kinds of patterns.
Somehow I did the same thing twice but in a different way, not realizing the original was in place (must have forgotten overnight and re-worked it in the morning, again, for the first time
😃 ) -- and you know what I did? I set the whole parent page functional component to conditionally return.
Like, literally, instead of having a line, I had the line, then the main functions' return -- so in checking to see if the user's state coming in from the provider was valid it showed or hid the entire mess.

I definitely owe you both a pizza or a drink. Just...wow. This one's not great for my impostor syndrome. At least the walk of shame will constitute exercise and keep me from becoming too sedentary.
Sep 13, 2022, 7:44 PM
Everyone brainfarts once in a while. 😄 ❤️
Sep 14, 2022, 7:01 AM
Yeah I just get self conscious when I don't keep track of how many brain beans I've been eating and strike up conversations with people trapped in a brain elevator with me 😆
Sep 14, 2022, 1:22 PM

Sanity– build remarkable experiences at scale

Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?