36

We keep getting random error messages via Airbreak from our React app while using React Intl for localization.

Error message

Invariant Violation: [React Intl] Could not find required intl object. needs to exist in the component ancestry.

Our application is very small. We have three pages, where one page has multiple modal dialogs embedded and then possibly opened by the user. We have been unable to pinpoint this problem to an exact browser, version or point in code.

We have tried to dig in to the error message and find the exact cause with no luck. Modal dialogs exist as a child component to the main one and are embedded when the parent component is rendered.

Our main.js render is wrapped as such with IntlProvider:

render() {
    return (
        <ErrorBoundary>
            <IntlProvider locale={language} messages={messages[language]}>
                <Router> 
                    <Route render={({location}) => (
                        <div id="site-container">
                                     ...

ErrorBoundary is Airbrake. IntlProvider gets the locale and translated strings.

Parent components have been injected with Intl:

Page.propTypes = {
intl: intlShape.isRequired
};

export default injectIntl(Page);

The IntlProvider usage is as the documentation describes it should be used but are we missing something here? What is the cause for this error? Some dialog is not getting the intl provided for it?

We should not be getting these errors. It appears to cause a total crash of any page load if it does.

Ken White
  • 123,280
  • 14
  • 225
  • 444
Rcls
  • 689
  • 2
  • 8
  • 13
  • Hard to say from the code you provided, but one guess is that you're not injecting intl in every component where you use `intl` prop. – Clarity Aug 20 '19 at 07:28
  • The only place we are not using injectIntl is the child components which should are embedded inside the parent render funcrtion like ''. Should MessageDialog components (children) also use InjectIntl? I believe I tried this once and it crashed because the parent already injects it. – Rcls Aug 20 '19 at 07:35
  • If you're using `intl` prop in your child component you might need to explicitly inject it there, or just pass it from the parent. Afaik, react-intl doesn't pass down injected intl to children automatically. – Clarity Aug 20 '19 at 07:38
  • I believe I already am as parent passes {... this.props} to all child components which contains intl already,. – Rcls Aug 20 '19 at 08:01
  • Well I found one component which gets called in our Header component that did not use injectIntl or get props passed to it as parent. This component is included in all the views, it's our language selection link container. If this is the cause it is very strange that we never picked this up during development and it only seems to occur very randomly at odd locations (we can also track URL's that these errors happen and seems to happen on all three pages) – Rcls Aug 20 '19 at 08:25
  • Did you find any root cause to this one ? – Victor Sep 21 '19 at 10:16
  • No, we still have errors in our production environment. – Rcls Sep 27 '19 at 05:54
  • I am running into the same thing currently, was a fix ever found? – caraclarke May 08 '20 at 13:47
  • I have the opposite situation... It happens only on my dev server. In production it works perfectly.. and this issue appeared after upgrading react-intl from v2 to 5 – sergioviniciuss Sep 08 '20 at 23:14
  • I'm also having this issue just showing up randomly and not at the same places everytime. It's really hard to catch, as there's no way to point at exactly where this warning comes from. – Jeggy Sep 14 '20 at 16:27
  • Have you add any luck resolving it? If yes can you share the solution pls – Baldráni May 17 '22 at 12:03

3 Answers3

2

For me it turned out that my ErrorBoundaries were out of scope IntlProvider

<Router>
    <SentryBoundary>
     <IntlProvider locale={this.state.lang} messages={localeData[this.state.lang]}>
        <Navbar lang={this.state.lang} changeLang={this.changeLang} />
        <div>
            <Route exact path="/" render={(props) => <Start{...props} />}/>
        </div>
     </IntlProvider>
    </SentryBoundary>
</Router>

All I had to do is just move Boundary INSIDE of IntlProvider

<Router>
  <IntlProvider locale={this.state.lang} messages={localeData[this.state.lang]}>
    <SentryBoundary>
        <Navbar lang={this.state.lang} changeLang={this.changeLang} />
        <div>
            <Route exact path="/" render={(props) => <Start{...props} />}/>
        </div>
    </SentryBoundary>
  </IntlProvider>
</Router>
Dhia Djobbi
  • 1,176
  • 2
  • 15
  • 35
Artem
  • 102
  • 7
1

Hard to say with provided code.

You have 2 options to retrieve intl object from components tree.

  1. injectIntl Intl object will be available in props when you inject it via injectIntl

    export default injectIntl(Component)

  2. useIntl hook Intl object will be available with useIntl

    const intl = useIntl()

Do not pass intl from parent to child via props.

Max G.
  • 733
  • 3
  • 10
0

This solution to fix this problem for me was to make sure to mount your test file with i18n every time you need to mount the component inside your jest tests. Here is an example:

it('set the value to 4 if changed value is not a number', () => {
  const wrapper = mountWithTestFileI18N(<Component {...props} />);
  wrapper.find('.form-input').simulate('change', { target: { value: 'tt' } });
  wrapper.find('.form-input').simulate('blur');

  expect(wrapper.find('.form-input').props().value).toBe(4);
});

You need to make sure to follow this mounting formula for every jest test. You should have your mounting function inside your jestsetup.js file. Here is an example of that:

function mountWithIntl(node, intlKeys, formatKeys, { context, childContextTypes } = 
    {}) {
  const { intl } = getIntlContext(intlKeys, formatKeys);
  return mount(nodeWithIntlProp(node, intl), {
    context: { ...context, intl },
    childContextTypes: { ...childContextTypes, intl: intlShape },
  });
}
Zach Pedigo
  • 396
  • 2
  • 9