I’m working on a project with react and i’m doing a simple history.push()
just to navigate to another component. Beside navigating to the other component i’m passion some data by using history.push({state: []})
.
To access the data to the other component I use useLocation()
and access it with location.state.
I switch the language on the app (the language logic is set with setLocalStorage()
) and suddenly the location.state
gets undefined
.
I read in some articles that location.state
gets undefined on re render. I didn’t find something clear that specify that, not even on the react-router-dom
documentation. Also when I reload the page the location.state
doesn’t become undefined
.
My question is why does it happen when the page is re render.?
Is it the best practice to pass the data like this? If not which is the best practice to pass it to another component so these errors can be avoid?
LanguageDropdown
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import { Dropdown } from 'react-bootstrap';
import classNames from 'classnames';
import enFlag from './flags/uk.jpg';
import italyFlag from './flags/italy.jpg';
import { useTranslation } from 'react-i18next';
// get the languages
const Languages = [
{
name: 'English',
flag: enFlag,
short_name: 'en'
},
{
name: 'Italian',
flag: italyFlag,
short_name: 'it'
}
];
const LanguageDropdown = () => {
const [dropdownOpen, setDropdownOpen] = useState<boolean>(false);
const [selectedLanguage, setSelectedLanguage] = useState(
localStorage.getItem('defaultLang') !== null
? Languages.find(function (item: { name: string; flag: string; short_name: string }) {
return item.short_name == localStorage.getItem('defaultLang');
})
: Languages[0]
);
const [t, i18n] = useTranslation('common');
/*
* toggle language-dropdown
*/
const toggleDropdown = () => {
setDropdownOpen(!dropdownOpen);
};
const changeLanguage = async (lang: any) => {
i18n.changeLanguage(lang.short_name);
setSelectedLanguage(lang);
localStorage.setItem('defaultLang', lang.short_name);
};
return (
<Dropdown show={dropdownOpen} onToggle={toggleDropdown}>
<Dropdown.Toggle
id='dropdown-languages'
as='a'
onClick={toggleDropdown}
className={classNames('nav-link waves-effect waves-light', {
show: dropdownOpen
})}>
<img src={selectedLanguage.flag} alt={selectedLanguage.name} height='16' />
</Dropdown.Toggle>
<Dropdown.Menu className='dropdown-menu dropdown-menu-end'>
<div onClick={toggleDropdown}>
{(Languages || []).map((lang, i) => {
return (
<Link
to='#'
onClick={() => changeLanguage(lang)}
className='dropdown-item notify-item'
key={i + '-lang'}>
<img src={lang.flag} alt={lang.name} className='me-1' height='12' />
<span className='align-middle'>{lang.name}</span>
</Link>
);
})}
</div>
</Dropdown.Menu>
</Dropdown>
);
};
export default LanguageDropdown;
2
Answers
The behavior you are observing where location.state becomes undefined on re-render is related to how React Router handles state objects passed via history.push(). The state is not intended to persist across re-renders of a component. Instead, it is meant to be used for passing temporary data from one route to another, and it doesn’t persist through navigation within the same route.
The location.state object is tied to the specific route you are navigating to. When you change the language using setLocalStorage(), it may trigger a re-render of the current component. If you are relying on location.state to persist between re-renders, it may appear as if it’s becoming undefined because it’s not a reliable way to store persistent data.
you can go for other alternatives:
If you need to persist data between page reloads, you can use localStorage or sessionStorage to store and retrieve data. This can be useful for storing user preferences like the selected language.
Issue
The issue appears to be the link targets used in the language selection dropdowns. They issue a navigation action to
"/#"
sans any existing state. This is the route state being wiped out.Solution
Instead of navigating to
"/#"
you may just navigate to the current locationIf you are effectively staying on the current page though, you could also just omit the
to
prop and cancel the navigation action.Demo