I am trying to migrate my application from React 0.12 to React 0.14 and am having trouble with option elements that use react-intl FormattedMessage objects placed inside select tags.
Here is a sample JSX code:
<select>
<option value="value1"><FormattedMessage message={this.getIntlMessage('key1')}/></option>
<option value="value2"><FormattedMessage message={this.getIntlMessage('key2')}/></option>
</select>
This code works fine in React 0.12 and I see my translated option elements.
In react 0.14, I got this error:
Only strings and numbers are supported as <option> children.
I traced the message to this changeset in React that happened earlier this year:
https://github.com/facebook/react/pull/3847/files
How can I fix this issue? I can't be the only one trying to use internationalized option elements?
This has always been an issue. React < 0.14 used to silently accept invalid DOM structure, in your case <span>
elements inside <option>
elements. The browser would then correct the DOM structure, and cause the virtual DOM managed by React to be out of sync with the real thing. You wouldn't see errors until you tried to re-render existing components instead of just re-mounting them.
react-intl
V2.0.0, which will ship with support for React 0.14, allows you to use the Function-As-Child pattern to customize the way your Formatted*
components render. See the "Function-As-Child Support" paragraph on this issue.
In your case, you would do:
<FormattedMessage message={this.getIntlMessage('key1')}>
{(message) => <option value="value1">{message}</option>}
</FormattedMessage>
<FormattedMessage message={this.getIntlMessage('key2')}>
{(message) => <option value="value2">{message}</option>}
</FormattedMessage>
I don't think there's a way to achieve this on the current stable version, 1.2.1.
With react-intl
v4.0.0 you can do this:
<select
className="content"
name="type-enquiry"
defaultValue="Type of Enquiry"
onChange={handleChange}
required
>
<option name="options" disabled hidden>
Choose
</option>
<FormattedMessage id='contact.enquiry.a' key={'op' + '-' + 'a'}>
{(message) => <option value='a'>{message}</option>}
</FormattedMessage>
<FormattedMessage id='contact.enquiry.b' key={'op' + '-' + 'b'}>
{(message) => <option value='b'>{message}</option>}
</FormattedMessage>
<FormattedMessage id='contact.enquiry.c' key={'op' + '-' + 'c'}>
{(message) => <option value='c'>{message}</option>}
</FormattedMessage>
</select>
example code
I had the same problem and solved it via the injectIntl().
This function is used to wrap a component and will inject the intl context object created by the IntlProvider as a prop on the wrapped component. Using the HOC factory function alleviates the need for context to be a part of the public API.
That means all you have to do is wrap your component with the injectIntl function, like that:
import React, {Component, PropTypes} from 'react';
import {defineMessages, injectIntl, intlShape} from 'react-intl';
const messages = defineMessages({
firstoption: {
id: 'mycomponent.firstoption',
defaultMessage: 'Coffee',
},
secondoption: {
id: 'mycomponent.secondoption',
defaultMessage: 'Tea',
}
});
class MyComponent extends Component {
render() {
const {formatMessage} = this.props.intl;
return (
<div>
<select>
<option value="value1">{formatMessage(messages.firstoption)}</option>
<option value="value2">{formatMessage(messages.secondoption)}</option>
</select>
</div>
);
}
}
MyComponent = {
intl : intlShape.isRequired
};
export default injectIntl(MyComponent)
Hope that helps...
As a little bit better alternative to @Alexandre Kirszenberg answer, it's also possible to inject intl
object into component and use formatMessage
function directly,
import { injectIntl, intlShape, defineMessages, FormattedMessage } from 'react-intl';
const AddressForm = ({ intl, street, number, postalCode, city, onChange }) => {
return (
<form id="paymentAddress">
// ...
<fieldset className="form-group">
<label htmlFor="country"><FormattedMessage { ...messages.country } />:</label>
<div>
<select name="country">
<option value="DE">{intl.formatMessage(messages.de)}</option>
<option value="UK">{intl.formatMessage(messages.uk)}</option>
<option value="CH">{intl.formatMessage(messages.ch)}</option>
</select>
</div>
</fieldset>
</form>
);
};
AddressForm.propTypes = {
intl: intlShape.isRequired,
// ...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With