15

I would like to use the formatMessage function of the react-intl API to insert a message as placeholder, but I can't figure out the right way to access this function.

Here is a simplified version of what I have:

// index.tsx
<IntlProvider locale={`fr-FR`} messages={messages_fr}>
    <NameForm/>
</IntlProvider>
// nameForm.tsx
interface NameFormProps {
    intl?: InjectedIntlProps,
}

export default injectIntl(NameForm);

class NameForm extends React.Component<NameFormProps, {}> {
    render() {
        let namePlaceholder = this.props.intl.formatMessage({
            id: "NAME_PLACEHOLDER",
            defaultMessage: "name"
        });
        
        return (
            <form>
                <input placeholder={namePlaceholder} type="text"/>
            </form>
        );
    }
}

I used InjectedIntlProps as type of the intl prop, because IntlShape didn't seem to provide a formatMessage method.

I Added a ? to the intl prop because I kept having a Property 'intl' is missing (but isn't injectIntl supposed to return a component without this prop?)

Now it compiles, but when running I get an error Cannot read property 'displayName' of undefined. I guess it's because the default export doesn't have an explicit name).

I feel I'm not going in the right direction, but can't find any example of a typescript/react-intl project.

Thanks for your help!

Yuriy Yakym
  • 3,616
  • 17
  • 30
Emarco
  • 1,385
  • 3
  • 12
  • 23

5 Answers5

15

While working with the same problem, I found that neither including InjectedIntlProps as a member, as mentioned in the question, nor extending from it, as mentioned in another answer, satisfies the type checker. When extending from InjectedIntlProps, the call to injectIntl checked, but using the resulting component in JSX expected me to provide an intl property. The following strategy resolved this though:

    interface NameFormProps {
        // Include all custom properties here.
    }

    class NameForm extends React.Component<NameFormProps & InjectedIntlProps, {}> {
        // Class body.
    }

    export default injectIntl(NameForm);
Braden Walters
  • 311
  • 2
  • 4
10

The problem was due to the version of the typescript definition. When using @types/react-intl": "^2.2.0", it works like a charm.

(edit) Few changes needed to make it work :

//index.tsx
<IntlProvider locale={`fr-FR`} messages={messages_fr}>
  <NameForm/>
</IntlProvider>,

//nameForm.tsx
interface NameFormProps extends InjectedIntlProps {
  placeholder: string,
}

class NameForm extends React.Component<NameFormProps, {}> {

  render() {
    let namePlaceholder = this.props.intl.formatMessage({
       id: this.props.placeholder,
       defaultMessage: "name"
      });

    return (
      <form>
        <input placeholder={namePlaceholder} type="text"/>
      </form>
    );
}

export default injectIntl(NameForm);
Emarco
  • 1,385
  • 3
  • 12
  • 23
  • Didn't work for me...but changing "extends React.Component" to "extends React.PureComponent" did. – karfus Apr 14 '17 at 21:09
  • Also important for me...the "export default" must come *after* the class being exported! – karfus Apr 14 '17 at 21:16
  • I edited it again. Indeed, you need to put the export line at the end of the file, and you can extends "InjectedIntlProps" instead of adding the intl props manually – Emarco Apr 26 '17 at 07:49
10

Updated:

After reading the docs about upgrade to 3.x, I found it has a simpler way, just use useIntl hook:

Sample code:

import { FormattedMessage, useIntl } from 'react-intl'


type Props = {
  placeholder: string,
}

function MyComponent({ placeholder }: Props) {
  const intl = useIntl()

  return (
    <input placeholder={intl.formateMessage({id: placeholder})} type="text"/>
  )
}

export default MyComponent

Old:

In the react-intl new version (3.12.0), we don't need @types/react-intl anymore, react-intl has its own type definition, and InjectedIntlProps doesn't exist anymore, it is named WrappedComponentProps instead.

My sample code:

import { FormattedMessage, injectIntl, WrappedComponentProps } from 'react-intl'

type Props = {
  placeholder: string,
} & WrappedComponentProps

function MyComponent({ placeholder, intl }: Props) {

  return (
    <input placeholder={intl.formateMessage({id: placeholder})} type="text"/>
  )
}

export default injectIntl(MyComponent)
Spark.Bao
  • 5,573
  • 2
  • 31
  • 36
7

Neither of the existing solutions worked for me. Instead it was due to injectIntl inferring the properties to include InjectedIntlProps.

To fix it, I had to explicitly tell injectIntl what props the wrapped component should have:

interface NameFormProps {
}

class NameForm extends React.Component<NameFormProps & InjectedIntlProps> {
}

export default injectIntl<NameFormProps>(NameForm);

If there are no props, it needs to be changed slightly:

class NameForm extends React.Component<InjectedIntlProps> {
}

export default injectIntl<{}>(NameForm);
Ryall
  • 12,010
  • 11
  • 53
  • 77
1

For those coming to this thread nowadays InjectedIntlProps type is no longer supported by current versions of react-intl. Instead use WrappedComponentProps

i.e

import { WrappedComponentProps } from 'react-intl';
  
interface IProps extends WrappedComponentProps {
  // other props
}
SakisTsalk
  • 796
  • 7
  • 18