0

We have been using react, react router, and redux. Now we want to add injectIntl. I am getting a problem with my syntax and hoping you could help.

import React, { Fragment } from 'react';
import { withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import { intlShape, injectIntl } from 'react-intl';

...

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(MyComponent));

and I changed it to be

export default injectIntl(withRouter(connect(mapStateToProps, mapDispatchToProps)(MyComponent)));

The error is

Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.

What do I need to do to combine all these frameworks?

Jason Hocker
  • 6,879
  • 9
  • 46
  • 79
  • The problem might be more to do with `MyComponent` rather than using all these dependencies together. What does `MyComponent` look like? – Paul. B Jun 08 '18 at 21:47

2 Answers2

0

It looks injectIntl doesn't hoist / copy the statics of the wrapped component and it's very possible reason of the problem.

Reacts docs says Static Methods Must Be Copied Over :

When you apply a HOC to a component, though, the original component is wrapped with a container component. That means the new component does not have any of the static methods of the original component.

You can check the already reported issue in injectIntl tracker.

There's a workaround, while the fix is officially applied to the project.

You can create a wrapper of injectIntl, that copies the statics correctly:

// utils/injectIntl.js

import { injectIntl as baseInjectIntl } from 'react-intl';
import hoistNonReactStatic from 'hoist-non-react-statics';

/**
 * A fixed injectIntl that hoists statics.
 */
export function injectIntl(WrappedComponent: Function): Function {
  const WrapperComponent = baseInjectIntl(WrappedComponent);

  hoistNonReactStatic(WrapperComponent, WrappedComponent);

  return WrapperComponent;
}

And later use it in your app, as follows:

import injectIntl from 'utils/injectIntl'

// ...

injectIntl(withRouter(connect(mapStateToProps, mapDispatchToProps)(MyComponent)));

Credits.

Jordan Enev
  • 16,904
  • 3
  • 42
  • 67
  • I gave this a try, and I get the same error message. – Jason Hocker Jun 06 '18 at 02:01
  • Now I see that `react-intl` docs says that you **must** use the provider component ``. Here are [implementation details](https://github.com/yahoo/react-intl/wiki#creating-an-i18n-context). You can try and please let me know if it works. – Jordan Enev Jun 06 '18 at 08:26
  • So before using `injectIntl`, you should firstly wrap your Root / App component with ``. – Jordan Enev Jun 06 '18 at 08:33
  • [I posted another answer](https://stackoverflow.com/a/50715922/4312466), that describes the must usage of `` – Jordan Enev Jun 06 '18 at 08:48
-1

Before using injectIntl, you should firstly wrap your Root / App component with <IntlProvider>.

ReactDOM.render(
    <IntlProvider
        locale={usersLocale}
        messages={translationsForUsersLocale}
    >
        <App/>
    </IntlProvider>,
    document.getElementById('container')
);

Here's what the react-intl docs says:

Creating an I18n Context

React Intl uses the provider pattern to scope an i18n context to a tree of components. This allows configuration like the current locale and set of translated strings/messages to be provided at the root of a component tree and made available to the <Formatted*> components. This is the same concept as what Flux frameworks like Redux use to provide access to a store within a component tree.

All apps using React Intl must use the <IntlProvider> component.

The most common usage is to wrap your root React component with <IntlProvider> and configure it with the user's current locale and the corresponding translated strings/messages

Jordan Enev
  • 16,904
  • 3
  • 42
  • 67