Skip to content

FormattedMessage vs formatMessage - re-rendering ? #585

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
amangeot opened this issue Aug 12, 2016 · 10 comments
Closed

FormattedMessage vs formatMessage - re-rendering ? #585

amangeot opened this issue Aug 12, 2016 · 10 comments
Labels

Comments

@amangeot
Copy link

amangeot commented Aug 12, 2016

Hello,
I have been using formatMessage, wrapping my components with injectIntl . This way when the locale changes, the component updates itself and its message translations:

<SomeLink to='/login' title={this.props.intl.formatMessage(login)} />

I just tried moving to FormattedMessage, removing injectIntl, but this way the component doesn't re-render when the locale changes:

<SomeLink to='/login' title={<FormattedMessage {...login} />} />

Is it possible to use FormattedMessage without injectIntl and still having the FormattedMessage update themselves when locale changes ?


This is how I setup IntlProvider in my redux app:

export const AppContainer = ({ store, history, routes, locale, messages }) => (
  <Provider store={store}>
    <IntlProvider locale={locale} messages={messages}>
      <MuiThemeProvider muiTheme={getMuiTheme(tealBaseTheme)}>
        <div style={{ height: '100%' }}>
          <Router history={history} children={routes} onUpdate={logPageView} />
        </div>
      </MuiThemeProvider>
    </IntlProvider>
  </Provider>
)
@ericf
Copy link
Collaborator

ericf commented Aug 15, 2016

<FormattedMessage> is built to re-render if anything with the intl context changes, like the locale.

@ericf
Copy link
Collaborator

ericf commented Aug 23, 2016

I just realized you're using a React element inside of an attribute named title, assuming this eventually is the value for an <a>'s title attribute, this won't work because it needs to be a string value.

So for this specific case you're correctly using injectIntl(). There's some more details about formatting messages to string values here: https://github.com/yahoo/react-intl/wiki/API#injection-api

@ericf ericf closed this as completed Aug 23, 2016
@ericf ericf added the question label Aug 23, 2016
@amangeot
Copy link
Author

amangeot commented Aug 25, 2016

SomeLink was built to receive strings or react elements like a FormattedMessage. I am still trying to understand why my code stops FormattedMessages from updating in some cases.

With the previous example, if a class SomeNav makes use of SomeLink passing a FormattedMessage as title props (not a string from formatMessage), the FormatedMessages will update on locale changes only if SomeNav is wrapped by injectIntl.

@amangeot
Copy link
Author

I have another case where a component, wrapped by injectIntl, renders several of these links:

<a className={...} onClick={...}>
   <div>{intl.formatMessage(messages)}</div>
</a>

I replaced one of these links with:

<a className={...} onClick={...}>
   <FormattedMessage {...message} />
</a>

When changing the locale, only strings from formatMessage will update.

Do you have any idea on what could happen ?

Packages:

  • react: 15.0.1,
  • react-intl: 2.1.2,
  • redux: 3.5.2,
  • react-redux: 4.4.5

@ericf
Copy link
Collaborator

ericf commented Aug 25, 2016

Maybe you're hitting this issue with React and shouldComponentUpdate? #234

@amangeot
Copy link
Author

amangeot commented Sep 1, 2016

Oh sorry I missed it, thank you for the link.

The workaround with IntlProvider key={locale} is one way to go, though it (usually) triggers data fetching as it unmounts / mounts components.

@ericf
Copy link
Collaborator

ericf commented Sep 2, 2016

@amangeot did that fix the issue? (I really hope this is fixed in React 16)

@amangeot
Copy link
Author

amangeot commented Sep 3, 2016

@ericf yes adding key={locale} to IntlProvider does fix the issue by re-mounting the entire app.

However it can add another issue as it unmounts / mounts all components. This triggers data fetching from Components' componentDidMount.

Another issue for my project is that I use react-google-maps with clusters which, currently, repaints the map 5000 times for 5000 markers, every time the map mounts or unmounts.

In order to avoid re-mounting components like this map or data-fetchers, I tried to apply the same logic but at a deeper level with a HOC like this one:

import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { getLocale } from 'store/modules/intl'

export const forceLocaleUpdate = (Component) => {
  const LocaleUpdater = ({ locale, ...props }) => <Component key={locale} {...props} />

  LocaleUpdater.propTypes = {
    locale: PropTypes.string.isRequired
  }

  const mapStateToProps = (state) => ({
    locale: getLocale(state)
  })

  return connect(mapStateToProps)(LocaleUpdater)
}

export default forceLocaleUpdate

For example, the following structure is to pick a time by dragging a div TimeLine:

<TimePicker>
  <TimeDragger>
    <TimeLine />
  </TimeDragger>
</TimePicker>

The position of TimeLine is controlled by TimeDragger and corresponds to a selected time. To avoid useless re-rendering, TimeLine should not update when time/position changes (when TimeDragger updates).

The problem is TimeLine uses some FormattedTime that will not update on locale changes. The HOC didn't help. Listening to locale changes in TimeLine's shouldComponentUpdate neither.

@ericf do you see a way to move the key={locale} logic to a deeper level to decrease side-effects / the number of components re-mounting ?

@ericf
Copy link
Collaborator

ericf commented Sep 3, 2016

Other things to look at is using injectIntl around the connected components, having the app extend Component instead of PureComponent. Do you expect users to change their locale often?

@amangeot
Copy link
Author

Sorry, I missed your message. Setting IntlProvider's key to locale is doing the job as I don't expect users to change their locale often. Thank you !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants