react-i18next: `useTranslation` returns an unstable reference to `t`

I tried searching for mentions of unstable reference to t returned by useTranslation and did not find anything. Also, the docs do not mention whether the reference is expected to be stable or not.

–>

🐛 Bug Report

useTranslation returns an unstable reference of t, this causes problems when using it on other hooks.

To Reproduce

const { t } = useTranslation('fakespace');
// when not ready, `t` is always a different function every time `useTranslation` is invoked.

Expected behavior

t should return a stable reference if arguments to useTranslation have not changed.

Your Environment

  • runtime version: i.e. node v14, webpack, chrome
  • i18next version: i.e. 19.8.4
  • os: Mac

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 10
  • Comments: 25 (12 by maintainers)

Most upvoted comments

@tigerabrodi The problem is for example if I’m using t in a useEffect to show a user a localized notification, I need to make sure I have the latest and correct reference to t in the useEffect hook, so I want to include t in its dependencies. If t is not stable, then it will cause my useEffect to needlessly rerun all the time. It doesn’t have to be a notification, it could be anything involving some string I need to localize in a hook that depends on t.

Roughly something like:

const { t } = useTranslation('fakespace');
useEffect(() => {
   // do something work
  // show a notification with a string localized using `t`
},
[t])

I’m curious if there is any plan/progress on this issue, the only workaround I came up with was totally remove t from dependency array of hooks like useEffect (it triggers API calls in my case) , don’t know if that has any negative consequences.

I did not find how to solve the problem but here we can see the problem with the less code possible: https://codesandbox.io/s/snowy-hill-vwljzl?file=/src/index.js

Open the terminal of the codesanbox and you’ll see:

  • 2 calls without “t” as dependency.
  • 3 calls with “t” as dependency.
  • 2 calls with “i18n”

As @kitsunekyo , I use t() to display a toast in useEffect and for now the only solution that I found is to use i18n.t()

NB: The first two renders are due to React 18: https://reactjs.org/docs/strict-mode.html#ensuring-reusable-state

Yeah, this one is a bit tricky, one thing is for sure though, we wanna memoize it haha 😄

@thiagoevg

do you really have an issue? or is there just something in your code that is sub-optimal?

looking at the problem and possible solution:

a) make t “stable” not changing if language changes or translations are loaded -> result your popup is filled with either no translations or wrong language depending on what triggered the change of t

b) not using t in the dependency array (which should not be named dependency array but props array -> as only those are needed to set there which trigger a rerender on change) -> same result as a) your text in the popup is wrong

c) don’t set the popup text but setPopupKey("Error_Key") -> and do based on that error key the translations inside the rendering of your popup

as a general rule -> keep translation as close as possible to the rendering and as far as possible from API calls

Just noticed this same problem on our project, a useEffect hook was constantly firing.

Switching from

const lang = useTranslation();
...
lang.t("foo");

to

const { t } = useTranslation();
...
t("foo");

solved the issue for us.

You got the t function mentioned in the issue -> than something is wrong in your code.

Nah sorry about that, I didn’t mean to get help with my code hahaha. What I meant was, I thought t is a function which takes an argument and depending on some global state (i18n object and its resources itc) returns a string, but seems it is more complicated than that.

I think the example was too small to make it obvs 😬 Thanks for the clarification @jamuhl

@eomuraliev if you end in if (!i18n) it actually tells you - your config/setup is broken!!! you should never get into that execution path - it only exists to warn you that you got something wrong.

t function gets only set if something changes, same for the ready boolean (which as a boolean is a type and not a ref)

Isn’t mandatory to pass i18n instance to react-i18next?

k is an argument, so you could get away with just wrapping notReady in a useCallback with an empty dependencies array. Also, you would need to lift the declaration of notReady up out of the if, since hooks should not be conditionally invoked.

I removed my guesses for where the problem is caused or how to solve it. I could be wrong on the causes, and I don’t want it to distract from the discussion that t should be stable.