简体   繁体   中英

Use react-i18next in class components with decorators and HOC

I'm trying to implement i18n in my React project which also uses Redux with the help of react-i18next .

In this project, we use class components with decorators.

Initially, I wanted to try react-i18next v10 but since it's relying on hooks and I cannot use those in class components, it's out of the question for me.

Falling back to the legacy v9, I followed the step by step guide ( https://react.i18next.com/legacy-v9/step-by-step-guide ) and performed the following steps:

  • Created the i18n.js config file with translations
  • Wrapped my root container with i18nextProvider
ReactDOM.render(
    <Provider store={store}>
        <Router history={history}>
            <I18nextProvider i18n={i18n}>
                <RootContainer />
            </I18nextProvider>
        </Router>
    </Provider>,
    $root,
);
  • Wrapped one simple component, which is a child of the RootContainer , with the withNamespaces() HOC, but with decorator syntax
@withNamespaces()
export default class SimpleComponent extends React.PureComponent {
    // (... component class code ...)
}

Which is equivalent without decorators to the following:

class SimpleComponent extends React.PureComponent {
    // (... component class code ...)
}
export default withNamespaces()(SimpleComponent);

I must be missing something since I get the following error during SSR:

react-i18next:: You will need pass in an i18next instance either by props, using I18nextProvider or by using i18nextReactModule. Learn more https://react.i18next.com/components/overview#getting-the-i-18-n-function-into-the-flow
0|ssr-dev  | TypeError: Cannot read property 'wait' of null
0|ssr-dev  |     at NamespacesConsumerComponent.render (/client/node_modules/react-i18next/dist/commonjs/NamespacesConsumer.js:220:33)

Problem is, I'm no longer having this error if I remove the @withNamespaces() HOC on my component class, but then I do not have {t, i18n} in the component's props and therefore cannot translate anything. Moreover, the given URL in the error unfortunately doesn't exist anymore.

As I understood from the documentation, the <I18nextProvider> is supposed to pass the {t, i18n} values down the component tree, which is not in my case.

I find myself stuck between not being able to use v10 (we have a lot of components and I can't just refactor them all to functional components at the moment) and not being able to make v9 work either.

I'm running out of options and could use some hindsight if you ever encountered similar issues.

Thanks in advance.

I managed to use the last version of react-i18next in class by doing like this:

import { withTranslation } from 'react-i18next';

class RootContainer extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
   <span>{this.props.t('Home')}</span>
  }
}

For anyone wondering, I got it working by wrapping the <RootContainer> child instead of wrapping <RootContainer> itself in the renderer method, which leaves me with something like the following:

  • Renderer
ReactDOM.render(
    <Provider store={store}>
        <Router history={history}>
            <RootContainer />
        </Router>
    </Provider>,
    $root,
);
  • RootContainer
import i18n from "../core/translations/i18n";
import {I18nextProvider} from "react-i18next";

@connectWithStore(mapStateToProps, mapActionsToProps)
export default class RootContainer extends React.PureComponent {
    // (... class code ...)

    render {
        return (
            <I18nextProvider i18n={i18n}>
                <div>
                    <SimpleComponent/>
                </div>
            </I18nextProvider>
        );
    }
}
  • SimpleElement
import {withNamespaces} from "react-i18next";

@withNamespaces()
export default class SimpleComponent extends React.PureComponent {
    // (... class code ...)

    componentDidMount() {
        // Successfully logging the values below
        console.warn("Got i18n props?", {
            t: this.props.t,
            i18n: this.props.i18n,
        });
    }
}

The only difference being that my renderer is a function, not a component per se, whereas RootContainer is a fully-fledged React component (PureComponent, to be precise). Maybe it has to do with render chaining, not sure, but it still works as intended that way.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM