Skip to content

Commit

Permalink
Merge pull request #1273 from medihack/replaceable-i18n-instance
Browse files Browse the repository at this point in the history
Fix allow to replace i18n in provider with useTranslation hook (#1272)
  • Loading branch information
jamuhl committed Mar 8, 2021
2 parents 254e43c + bd657b6 commit 13c95ce
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 6 deletions.
24 changes: 18 additions & 6 deletions src/useTranslation.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {};
Expand All @@ -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;
Expand All @@ -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() {
Expand Down Expand Up @@ -68,12 +70,22 @@ 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

// t is correctly initialized by useState hook. We only need to update it after i18n
// instance was replaced (for example in the provider).
const isInitial = useRef(true);
useEffect(() => {
if (isMounted.current && !isInitial.current) {
setT(getT());
}
isInitial.current = false;
}, [i18n]); // re-run when i18n instance was replaced

const ret = [t.t, i18n, ready];
ret.t = t.t;
ret.i18n = i18n;
Expand All @@ -86,7 +98,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();
});
Expand Down
25 changes: 25 additions & 0 deletions test/useTranslation.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) => (
<I18nextProvider i18n={i18n}>{children}</I18nextProvider>
);

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');
});
});
});

0 comments on commit 13c95ce

Please sign in to comment.