Double render of components with react 18 after call to `i18n.activate`
See original GitHub issueDescribe 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 yarnyarn dev
(or npm). - Go to
http://localhost:3000/
and see only one H1 withWelcome to SWLP
- Press F5
- See the H1 twice
- Comment out or delete line 36 in
_app.tsx
which readsi18n_.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:
- Created a year ago
- Reactions:2
- Comments:9 (1 by maintainers)
Top GitHub Comments
@Cielquan Try adding
forceRenderOnLocaleChange=false
toI18nProvider
. Like this:Does it solve your problem?
Hey @stovmascript I switched to the
i18next
framework and usenext-i18next
, which is a wrapper forreact-i18next
, for my nextJS project.Yes you can use natural language for the translation keys.
Notable differenances are:
t
is a function here insteadi18next-gettext-converter
lingui extract
command, but there are two extraction toolsi18next-parser
andi18next-scanner
where I use the first oneThere are some caveats with the
i18next-parser
tough, but I did not test, if thouse also apply if you create the translations manually:t()
function keep their line breaks and spaces when indented