question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Double render of components with react 18 after call to `i18n.activate`

See original GitHub issue

Describe the bug I have a TypeScript nextJS app, which uses lingui for translation. I wanted to migrate from react 17 to 18 and got the issue that components got rendered twice above each other. I tracked the issue down to my HOC, which adds the I18nProvider and then tracked it further down to the call to i18n.activate(). When I comment out the line which calls i18n.activate() the double render does not happen anymore.

This currently seems to be the only issue keeping us from migrating to react 18.

To Reproduce

  • Git clone this minimal repo I cut down from the original app: https://github.com/Cielquan/react-18-lingui-double-render-issue
  • Install dependencies
  • Run the dev script with yarn yarn dev (or npm).
  • Go to http://localhost:3000/ and see only one H1 with Welcome to SWLP
  • Press F5
  • See the H1 twice
  • Comment out or delete line 36 in _app.tsx which reads i18n_.activate(locale);
  • Let the app auto relead
  • Press F5 in the browser
  • See only one H1 even after repeated F5 presses

Content of _app.tsx:

import { i18n } from "@lingui/core";
import type { I18n } from "@lingui/core";
import { I18nProvider } from "@lingui/react";
import type { AppProps } from "next/app";
import { useEffect, useRef } from "react";
import type { DependencyList } from "react";

const useUpdateEffect = (
  callback: () => void,
  dependencies: DependencyList,
  cleanupCallback?: () => void
): void => {
  const firstRenderRef = useRef(true);

  useEffect(() => {
    if (firstRenderRef.current) {
      firstRenderRef.current = false;
      return undefined;
    }
    callback();
    return cleanupCallback;
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, dependencies);
};

async function changeLocale(locale: string, i18n_: I18n): Promise<void> {
  let data;
  if (process.env.NODE_ENV === "production") {
    data = await import(`../../translations/locales/${locale}/messages`);
  } else {
    data = await import(`@lingui/loader!../../translations/locales/${locale}/messages.po`);
  }
  const { messages } = data;

  i18n_.load(locale, messages);
  i18n_.activate(locale);
}

const App = ({ Component, pageProps }: AppProps): React.ReactElement => {
  const locale = "en-US";

  // run only once on the first render (for server side)
  const firstRender = useRef(true);
  if (firstRender.current) {
    // loadPlurals(i18n);
    changeLocale(locale, i18n);
    firstRender.current = false;
  }

  useUpdateEffect(() => {
    changeLocale(locale, i18n);
  }, [i18n, locale]);

  return (
    <I18nProvider i18n={i18n}>
      <Component {...pageProps} />
    </I18nProvider>
  );
};

export default App;

Expected behavior I expect the H1 with Welcome to SWLP to only be rendered once.

Additional context

  • lingui: “3.14.0”
  • next: “12.2.5”
  • react: “18.2.0”
  • babel/core: “7.13.10”
  • babel config: {"presets": ["next/babel"], "plugins": ["macros"]}

I know about the double calls to useEffect in react 18 strict mode, but I do not see an issue this this here.

EDIT: Add information

Issue Analytics

  • State:open
  • Created a year ago
  • Reactions:2
  • Comments:9 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
akozhemiakincommented, Oct 20, 2022

@Cielquan Try adding forceRenderOnLocaleChange=false to I18nProvider. Like this:

<I18nProvider i18n={i18n} forceRenderOnLocaleChange={false}>
    <Component {...pageProps}/>
</I18nProvider>

Does it solve your problem?

0reactions
Cielquancommented, Nov 6, 2022

Hey @stovmascript I switched to the i18next framework and use next-i18next, which is a wrapper for react-i18next, for my nextJS project.

Yes you can use natural language for the translation keys.

Notable differenances are:

  • no use of babel macros: t is a function here instead
  • JSON instead of po files for translations (I really like the gettext file format); but there is a converter tool i18next-gettext-converter
  • no direct extration of translation keys from code like lingui extract command, but there are two extraction tools i18next-parser and i18next-scanner where I use the first one

There are some caveats with the i18next-parser tough, but I did not test, if thouse also apply if you create the translations manually:

  • the parser does not execute the code but rather extract the stuff via regex or something I guess
  • multiline strings given to the t() function keep their line breaks and spaces when indented
  • direct interpolation of values in template strings does not work, but you only need to change the function call
const someName = "Cielquan";

// lingui
t`Hello {someName}`

// i18next
t("Hello {{someName}}", {someName}) // values are passed as an object
Read more comments on GitHub >

github_iconTop Results From Across the Web

Bug: confused with react 18 render twice #24425 - GitHub
React 18 renders your component twice in development mode. This is done to detect problems with purity. Your component has to be pure....
Read more >
React 18 strict mode causing component to render twice
React StrictMode calls all Effects twice to make sure their cleanup/unmount handlers work as intended. You may need to change your effects ...
Read more >
Step by step guide (v9) - react-i18next documentation
We have installed the needed i18n packages react-i18next and i18next: ... notice the initial render will trigger the load for page2.json but also...
Read more >
A Guide to React Localization with i18next | Phrase
We'll cover how to localize both types of components a bit later. Demo app header | Phrase. Our rendered header. As mentioned before,...
Read more >
Solving the React 18 Double Render problem on useEffect
React 18's useEffect hook now double renders (with the empty dependency array) which has caused a stir in the React community.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found