简体   繁体   中英

React (typescript) hooks order of execution

I'm struggling with a language switching feature. The home page of my app at / should pickup a previously localstorage setting, 'preferredLanguage' , or pick up a default language from navigator.language if no preference is set. It should be able to handle lang routes /cat and /es that set that preference and the page language and redirect to /.

It looks like this:

A useLang hook that sets the preferred and switches lang


export const useLang = (lang: string) => {
  const { t, i18n } = useTranslation()

  useEffect(() => {
    console.log(`Setting lang to ${lang}`);
    localStorage.setItem('preferredLanguage', lang);
    i18n.changeLanguage(lang);
    document.title = t('PageTitle');
  }, [])

}

App.tsx

const SetLang = ({ lang }: {lang: string}) => {
  useLang(lang);
  return (
    <Redirect to='/'/>
  )
}

export const App = () => {
  const { t } = useTranslation();
  const targetLang = localStorage.getItem('preferredLanguage') ?? navigator.language.includes("es") ? "es" : "cat";

  // Debug: 
  useEffect(() => {
    console.log(`Preferred: ${localStorage.getItem('preferredLanguage')}, Default: ${navigator.language.includes("es") ? "es" : "cat"}, Will set lang to ${targetLang}`);
  }, []);

  useLang(targetLang); // Y U NO WORK?

  return (
    <Router>
      <Switch>
        <Route exact path="/es" >
          <SetLang lang="es" />
        </Route>
        <Route exact path="/cat" >
          <SetLang lang="cat" />
        </Route>
        <Route exact path="/">
          <HomePage/>
        </Route>
        // ...etc
      </Switch>
    <Router>
  )


Executing gives the following logging:

Preferred: es, Default: es, Will set lang to es App.tsx:25
Setting lang to es

Then I go to /cat and expect it to switch to catalan but a subsequent call occurs switching it back to es:

Setting lang to cat 
Preferred: cat, Default: es, Will set lang to es
Setting lang to es

I'm confused. Something is not behaving as I expect. I see that the expression localStorage.getItem('preferredLanguage') ?? navigator.language.includes("es") ? "es" : "cat"; localStorage.getItem('preferredLanguage') ?? navigator.language.includes("es") ? "es" : "cat"; should resolve to 'cat' if it is present in localStorage as the logging suggests. After page reloading, I check localStorage and yields 'es', as expected from the last call to useLang with the unintended 'es'.

What am I missing?

Thanks

Your deps for useLang should include lang also. I'm pretty sure that you get a warning about this actually. Try changing it to this:

export const useLang = (lang: string) => {
  const { t, i18n } = useTranslation()

  useEffect(() => {
    console.log(`Setting lang to ${lang}`);
    localStorage.setItem('preferredLanguage', lang);
    i18n.changeLanguage(lang);
    document.title = t('PageTitle');
  }, [lang])

}

I wasn't using hooks correctly. Now I defined a function within it and returned it, confining effectful code into that function.

import {useTranslation} from 'react-i18next';

export const useLang = () => {
  const { t, i18n } = useTranslation()

  const setLang = (lang?: string) => {
    const preferredLang = localStorage.getItem('preferredLanguage')
    const defaultLang = navigator.language.includes("es") ? "es" : "cat";
    const targetLang = preferredLang ?? defaultLang;

    console.log(`Preferred: ${localStorage.getItem('preferredLanguage')}, Default: ${navigator.language.includes("es") ? "es" : "cat"}, Will set lang to ${targetLang}`);

    if(lang){
      localStorage.setItem('preferredLanguage', lang);
      i18n.changeLanguage(lang);
    }else{
      localStorage.setItem('preferredLanguage', targetLang);
      i18n.changeLanguage(targetLang);
    }
    document.title = t('PageTitle');
  }

  return {setLang}

}

export const App = () => {
  const {setLang} = useLang();
  
  useEffect(() => {
    setLang(); // TY!
  }, []);

  return (...)
}

Note that even though preferredLang and defaultLang were updating (previously they were just expressions), targetLang was not since it was closured over the hook. Thus yielding the weird situation resulting in the Preferred: cat, Default: es, Will set lang to es line (weird since it suggested that 'cat' ?? 'es' resolved to 'es' )

Thanks

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM