skip to Main Content

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


  1. 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.

    Login or Signup to reply.
  2. 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 location

    const LanguageDropdown = () => {
      const location = useLocation(); // <-- current location, including any state
    
      ...
    
      return (
        <Dropdown show={dropdownOpen} onToggle={toggleDropdown}>
          ...
          <Dropdown.Menu className='dropdown-menu dropdown-menu-end'>
            <div onClick={toggleDropdown}>
              {(Languages || []).map((lang, i) => {
                return (
                  <Link
                    to={location} // <-- current location, including any state
                    onClick={() => changeLanguage(lang)}
                    className='dropdown-item notify-item'
                    key={i + '-lang'}
                  >
                    ...
                  </Link>
                );
              })}
            </div>
          </Dropdown.Menu>
        </Dropdown>
      );
    };
    
    export default LanguageDropdown;
    

    If you are effectively staying on the current page though, you could also just omit the to prop and cancel the navigation action.

    const LanguageDropdown = () => {
      ...
    
      return (
        <Dropdown show={dropdownOpen} onToggle={toggleDropdown}>
          ...
          <Dropdown.Menu className='dropdown-menu dropdown-menu-end'>
            <div onClick={toggleDropdown}>
              {(Languages || []).map((lang, i) => {
                return (
                  <Link
                    onClick={(e) => {
                      e.preventDefault(); // <-- prevent navigation action
                      changeLanguage(lang);
                    }}
                    className='dropdown-item notify-item'
                    key={i + '-lang'}
                  >
                    ...
                  </Link>
                );
              })}
            </div>
          </Dropdown.Menu>
        </Dropdown>
      );
    };
    
    export default LanguageDropdown;
    

    Demo

    Edit why-location-state-gets-undefined-when-the-page-reloads

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search