Reputation: 1928
I would like to use react-i18next with my react-redux connected component and am not sure how to go about it.
I've simplified my code to show an example of a connected component:
import React from 'react';
import {connect} from 'react-redux';
import {userSelectors} from "./userSelectors";
interface IConnectedProps {
activeUserName: string | undefined;
}
export class LandingPageComponent extends React.Component<IConnectedProps> {
public render(): JSX.Element {
return (
<React.Suspense fallback={<Spinner/>}>
<React.Fragment>
<div>
... a bunch of controls using translated text
</div>
<div>{activeUserName}</div>
</React.Fragment>
</React.Suspense>
);
}
}
const mapStateToProps = (state: ICoreRootState) : IConnectedProps => ({
activeUserName: userSelectors.getDisplayName(state),
});
export const LandingPage = connect(mapStateToProps)(LandingPageComponent);
Installed package versions:
react version: 16.8.4
react-redux version: 5.1.1
react-i18next version: 10.6.0
What I've tried:
1) I get the error below when I use withTranslation, WithTranslation as follows:
export class LandingPageComponent extends React.Component<IConnectedProps & WithTranslation> {...}
export const LandingPage = connect(mapStateToProps)(withTranslation()(LandingPageComponent));
Error:
The above error occurred in the <withI18nextTranslation(LandingPageComponent)> component:
in withI18nextTranslation(LandingPageComponent) (created by Connect(withI18nextTranslation(LandingPageComponent)))
in Connect(withI18nextTranslation(LandingPageComponent))
in Route
in t
in Connect(t) (at App.tsx:49)
in Switch (at App.tsx:45)
in App (at src/index.tsx:14)
in Router (created by ConnectedRouter)
in ConnectedRouter (created by Connect(ConnectedRouter))
in Connect(ConnectedRouter) (at src/index.tsx:13)
in Provider (at src/index.tsx:12)
2) I get the error below when I use withTranslation, WithTranslation as follows:
export class LandingPageComponent extends React.Component<IConnectedProps & WithTranslation> {...}
export const LandingPage = withTranslation()(connect(mapStateToProps)(LandingPageComponent));
Error:
index.js:1446 The above error occurred in the <withI18nextTranslation(Connect(LandingPageComponent))> component:
in withI18nextTranslation(Connect(LandingPageComponent))
in Route
in t
in Connect(t) (at App.tsx:49)
in Switch (at App.tsx:45)
in App (at src/index.tsx:14)
in Router (created by ConnectedRouter)
in ConnectedRouter (created by Connect(ConnectedRouter))
in Connect(ConnectedRouter) (at src/index.tsx:13)
in Provider (at src/index.tsx:12)
3) I cannot use useTranslation since hooks are not allowed to be used within a class.
I also tried the following:
... a bunch of imports
interface ILogoutButtonProps {
userName?: string;
}
interface IConnectedHandlers {
readonly logout: any;
readonly push: any;
}
class InnerLogoutComponent extends React.Component<IButtonProps & IConnectedHandlers & ILogoutButtonProps & WithTranslation, {}> {
public render() {
const {userName, onClick, logout: Logout, push: Push, ...buttonProps} = this.props;
const logoutText = this.props.i18n.t(StringNames.logout);
const buttonText = userName ? logoutText + " " + userName : logoutText;
return (
<Button {...buttonProps} text={buttonText} onClick={this.handleClick}/>
);
}
private handleClick = (event: React.MouseEvent<HTMLElement>) : void => {
this.props.logout()
.then(() => this.props.push(LoginPaths.verifyUser));
}
}
const InnerLogoutTranslatedComponent = withTranslation()(InnerLogoutComponent);
class LogoutComponentInternal extends React.Component<IButtonProps & IConnectedHandlers & ILogoutButtonProps, {}> {
public render () {
return (
<InnerLogoutTranslatedComponent {...this.props}/>
);
}
}
export const LogoutComponent = connect(null,{logout, push})(LogoutComponentInternal);
but I get the following error:
Hooks can only be called inside the body of a function component.
Thank you in advance...
Upvotes: 23
Views: 10854
Reputation: 1333
This works for me:
export default withTranslation()(connect(mapStateToProps, mapDispatchToProps)(Component));
Upvotes: 1
Reputation: 346
I am using SSR with RazzleJS and in my case its working perfectly fine. I connected my connect
and withTranslation
like this:
export default connect(mapStateToProps,mapDispatchToProps)(withTranslation()(Component));
Upvotes: 5
Reputation: 495
In our project, we successfully utilize this:
import { compose } from 'redux';
import { withNamespaces } from 'react-i18next';
import { connect } from 'react-redux';
...
export default compose(withNamespaces('translation'), connect(mapStateToProps))(ComponentName);
With this, we connect to Redux with mapStateToProps and we have translations.
Upvotes: 5
Reputation: 8773
In my case, I fixed it by doing:
export default withTranslation(null, {withRef: true})(MyComponent);
withRef
is false
by default.
Source: https://github.com/i18next/react-i18next/blob/master/src/withTranslation.js
Upvotes: 0
Reputation: 5227
I'm actually having trouble to determine in which order you wrap your components in HOCs. In the project I'm currently working in we wrap like withNamespaces(connect(withStyles(component)))
, which works really well (withNamespaces
is essentially the same as withTranslations
). We had issues when trying to connect a translated component, you are possibly experiencing the same issues right now. So here's our way of doing it:
You have a "normal" component like
type InjectedProps = StateProps & ExternalProps & MyComponentsTranslations
export class MyComponent extends React.Component<InjectedProps> {
...
}
(Note: the procedure works exactly the same with functional components)
you can const MyConnectedComponent = connect(mapStateToProps, mapDispatchToProps)(MyComponent)
and finally, you do a
import {WithNamespaces, withNamespaces} from "react-i18next"
export const LocalizedMyComponent = withNamespaces()(
({t,...rest}): WithNamepsaces) => (
<MyConnectedComponent translations={{ put translations here }} {...rest} />
)
)
Now, the trick is that we define an interface MyComponentsTranslations {}
where we put all the required translations or translate functions (in case of plurals).
MyComponentsTranslations
is added to the InjectedProps
to make them available in the original component.
You can always simply inject the t
-function of i18n into your components, but in my current project we decided that it is much cleaner to
Let me know if this works for you.
Also, to make the whole thing a little bit more elegant, you can use these helpers:
export interface Translations<T> {
translations: T
}
export const createTranslations = <T>(translations: T): Translations<T> => ({
translations,
})
This allows you to set
type InjectedProps = StateProps & Translations<MyComponentTranslations>
and in the withNamespace
hoc:
<MyConnectedComponent {...createTranslations<MyComponentTranslations>({ put translations here })} {...rest} />
Upvotes: 0