diff --git a/packages/react/src/I18nProvider.test.tsx b/packages/react/src/I18nProvider.test.tsx index 0ba70bb62..f22df660c 100644 --- a/packages/react/src/I18nProvider.test.tsx +++ b/packages/react/src/I18nProvider.test.tsx @@ -3,7 +3,7 @@ import { act, render } from "@testing-library/react" import { I18nProvider, useLingui } from "./I18nProvider" import { I18n, setupI18n } from "@lingui/core" -import { useMemo } from "react" +import { useMemo, useCallback } from "react" describe("I18nProvider", () => { it( @@ -226,4 +226,40 @@ describe("I18nProvider", () => { expect(getByText("Ahoj světe")).toBeTruthy() }) + + it("keeps memoized useLingui().i18n locale in sync on locale change", () => { + const i18n = setupI18n({ + locale: "en", + messages: { + en: {}, + cs: {}, + }, + }) + + const ComponentWithMemoizedI18n = () => { + const { i18n } = useLingui() + + const getLocale = useCallback( + (i18nInstance: I18n) => i18nInstance.locale, + [], + ) + const currentLocale = useMemo(() => getLocale(i18n), [getLocale, i18n]) + + return
{currentLocale}
+ } + + const { getByTestId } = render( + + + , + ) + + expect(getByTestId("locale").textContent).toBe("en") + + act(() => { + i18n.activate("cs") + }) + + expect(getByTestId("locale").textContent).toBe("cs") + }) }) diff --git a/packages/react/src/I18nProvider.tsx b/packages/react/src/I18nProvider.tsx index a8e3b5791..142e77f42 100644 --- a/packages/react/src/I18nProvider.tsx +++ b/packages/react/src/I18nProvider.tsx @@ -54,17 +54,20 @@ export const I18nProvider = ({ * of creating a separate Provider/Consumer pair. * * We can't use useMemo hook either, because we want to recalculate value manually. + * + * We wrap `i18n` in a Proxy to create a new reference on each context update. + * This ensures React correctly invalidates memoized values that depend on `i18n`. */ const makeContext = useCallback( () => ({ - i18n, + i18n: new Proxy(i18n, {}), defaultComponent, _: i18n.t.bind(i18n), }), [i18n, defaultComponent], ) - const [context, setContext] = useState(makeContext()) + const [context, setContext] = useState(makeContext) /** * Subscribe for locale/message changes