diff --git a/src/useTranslation.js b/src/useTranslation.js index 8ba619d53..25e2a9474 100755 --- a/src/useTranslation.js +++ b/src/useTranslation.js @@ -10,7 +10,7 @@ export function useTranslation(ns, props = {}) { if (i18n && !i18n.reportNamespaces) i18n.reportNamespaces = new ReportNamespaces(); if (!i18n) { warnOnce('You will need to pass in an i18next instance by using initReactI18next'); - const notReadyT = k => (Array.isArray(k) ? k[k.length - 1] : k); + const notReadyT = (k) => (Array.isArray(k) ? k[k.length - 1] : k); const retNotReady = [notReadyT, {}, false]; retNotReady.t = notReadyT; retNotReady.i18n = {}; @@ -19,7 +19,9 @@ export function useTranslation(ns, props = {}) { } if (i18n.options.react && i18n.options.react.wait !== undefined) - warnOnce('It seems you are still using the old wait option, you may migrate to the new useSuspense behaviour.'); + warnOnce( + 'It seems you are still using the old wait option, you may migrate to the new useSuspense behaviour.', + ); const i18nOptions = { ...getDefaults(), ...i18n.options.react, ...props }; const { useSuspense } = i18nOptions; @@ -34,7 +36,7 @@ export function useTranslation(ns, props = {}) { // are we ready? yes if all namespaces in first language are loaded already (either with data or empty object on failed load) const ready = (i18n.isInitialized || i18n.initializedStoreOnce) && - namespaces.every(n => hasLoadedNamespace(n, i18n, i18nOptions)); + namespaces.every((n) => hasLoadedNamespace(n, i18n, i18nOptions)); // binding t function to namespace (acts also as rerender trigger) function getT() { @@ -68,12 +70,20 @@ export function useTranslation(ns, props = {}) { // unbinding on unmount return () => { isMounted.current = false; - if (bindI18n && i18n) bindI18n.split(' ').forEach(e => i18n.off(e, boundReset)); + if (bindI18n && i18n) bindI18n.split(' ').forEach((e) => i18n.off(e, boundReset)); if (bindI18nStore && i18n) - bindI18nStore.split(' ').forEach(e => i18n.store.off(e, boundReset)); + bindI18nStore.split(' ').forEach((e) => i18n.store.off(e, boundReset)); }; }, [namespaces.join()]); // re-run effect whenever list of namespaces changes + const isInitial = useRef(true); + useEffect(() => { + if (isMounted.current && !isInitial.current) { + setT(getT()); + } + isInitial.current = false; + }, [i18n]); + const ret = [t.t, i18n, ready]; ret.t = t.t; ret.i18n = i18n; @@ -86,7 +96,7 @@ export function useTranslation(ns, props = {}) { if (!ready && !useSuspense) return ret; // not yet loaded namespaces -> load them -> and trigger suspense - throw new Promise(resolve => { + throw new Promise((resolve) => { loadNamespaces(i18n, namespaces, () => { resolve(); }); diff --git a/test/useTranslation.spec.js b/test/useTranslation.spec.js index 282712a56..2955cc1db 100644 --- a/test/useTranslation.spec.js +++ b/test/useTranslation.spec.js @@ -113,4 +113,29 @@ describe('useTranslation', () => { expect(i18nInstance.reportNamespaces.getUsedNamespaces()).toContain(namespace); }); }); + + describe('replacing i18n instance in provider', () => { + i18nInstance.addResource('fr', 'translation', 'key1', 'test2'); + const i18nInstanceClone = i18nInstance.cloneInstance({ lng: 'fr' }); + const wrapper = ({ children, i18n }) => ( + {children} + ); + + it('should render correct content', () => { + const { result, rerender } = renderHook(() => useTranslation(), { + wrapper, + initialProps: { + i18n: i18nInstance, + }, + }); + + const { t: t1 } = result.current; + expect(t1('key1')).toBe('test'); + + rerender({ i18n: i18nInstanceClone }); + + const { t: t2 } = result.current; + expect(t2('key1')).toBe('test2'); + }); + }); });