Skip to content

Commit 227a444

Browse files
authored
feat: simplify provider inheritance, add new APIs (#1387)
Also introduces `createIntl` and `RawIntlProvider` fixes #1386 fixes #1376 ## Creating intl without using Provider We've added a new API called `createIntl` that allows you to create an `IntlShape` object without using `Provider`. This allows you to format things outside of React lifecycle while reusing the same `intl` object. For example: ```tsx import {createIntl, createIntlCache, RawIntlProvider} from 'react-intl' // This is optional but highly recommended // since it prevents memory leak const cache = createIntlCache() const intl = createIntl({ locale: 'fr-FR', messages: {} }, cache) // Call imperatively intl.formatNumber(20) // Pass it to IntlProvider <RawIntlProvider value={intl}>{foo}</RawIntlProvider> ``` This is especially beneficial in SSR where you can reuse the same `intl` object across requests.
1 parent 05970c2 commit 227a444

22 files changed

+295
-289
lines changed

docs/API.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ There are a few API layers that React Intl provides and is built on. When using
1212
- [`useIntl` hook (currently available in 3.0.0 beta)](#useintl-hook-currently-available-in-300-beta)
1313
- [`injectIntl` HOC](#injectintl-hoc)
1414
- [`IntlShape`](#intlshape)
15+
- [`createIntl`](#createintl)
1516
- [Date Formatting APIs](#date-formatting-apis)
1617
- [`formatDate`](#formatdate)
1718
- [`formatTime`](#formattime)
@@ -86,6 +87,7 @@ React Intl provides:
8687

8788
1. [`useIntl` hook](#useintl-hook): to _hook_ the imperative formatting API into a React function component (with React version >= 16.8).
8889
2. [`injectIntl` HOC](#injectintl-hoc): to _inject_ the imperative formatting API into a React class or function component via its `props`.
90+
3. [`createIntl`](#createintl): to create `IntlShape` object outside of React lifecycle.
8991

9092
These should be used when your React component needs to format data to a string value where a React element is not suitable; e.g., a `title` or `aria` attribute, or for side-effect in `componentDidMount`.
9193

@@ -198,6 +200,29 @@ The definition above shows what the `props.intl` object will look like that's in
198200
- **`IntlConfig`:** The intl metadata passed as props into the parent `<IntlProvider>`.
199201
- **`IntlFormatters`:** The imperative formatting API described below.
200202

203+
#### `createIntl`
204+
205+
This allows you to create an `IntlShape` object without using `Provider`. This allows you to format things outside of React lifecycle while reusing the same `intl` object. For example:
206+
207+
```tsx
208+
import {createIntl, createIntlCache, RawIntlProvider} from 'react-intl'
209+
210+
// This is optional but highly recommended
211+
// since it prevents memory leak
212+
const cache = createIntlCache()
213+
214+
const intl = createIntl({
215+
locale: 'fr-FR',
216+
messages: {}
217+
}, cache)
218+
219+
// Call imperatively
220+
intl.formatNumber(20)
221+
222+
// Pass it to IntlProvider
223+
<RawIntlProvider value={intl}>{foo}</RawIntlProvider>
224+
```
225+
201226
### Date Formatting APIs
202227

203228
React Intl provides three functions to format dates:

docs/Components.md

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ React Intl has a set of React components that provide a declarative way to setup
77
- [Why Components?](#why-components)
88
- [Intl Provider Component](#intl-provider-component)
99
- [`IntlProvider`](#intlprovider)
10-
- [Multiple Intl Contexts](#multiple-intl-contexts)
10+
- [`RawIntlProvider`](#rawintlprovider)
1111
- [Dynamic Language Selection](#dynamic-language-selection)
1212
- [Date Formatting Components](#date-formatting-components)
1313
- [`FormattedDate`](#formatteddate)
@@ -109,9 +109,25 @@ Assuming `navigator.language` is `"fr"`:
109109
<div><span>mardi 5 avril 2016</span></div>
110110
```
111111

112-
#### Multiple Intl Contexts
112+
### `RawIntlProvider`
113113

114-
Nested `<IntlProvider>` components can be used to provide a different, or modified i18n context to a subtree of the app. In these cases, the nested `<IntlProvider>` will inherit from its nearest ancestor `<IntlProvider>`. A nested strategy can be employed to provide a subset of translations to a subtree. See: [Nested Example app](https://github.com/formatjs/react-intl/tree/master/examples/nested)
114+
This is the underlying `React.Context.Provider` object that `IntlProvider` use. It can be used in conjunction with `createIntl`:
115+
116+
```tsx
117+
import {createIntl, createIntlCache, RawIntlProvider} from 'react-intl'
118+
119+
// This is optional but highly recommended
120+
// since it prevents memory leak
121+
const cache = createIntlCache()
122+
123+
const intl = createIntl({
124+
locale: 'fr-FR',
125+
messages: {}
126+
}, cache)
127+
128+
// Pass it to IntlProvider
129+
<RawIntlProvider value={intl}>{foo}</RawIntlProvider>
130+
```
115131

116132
#### Dynamic Language Selection
117133

docs/Testing-with-React-Intl.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ import expect from 'expect';
136136
import expectJSX from 'expect-jsx';
137137
import React from 'react';
138138
import {createRenderer} from 'react-addons-test-utils';
139-
import {IntlProvider, FormattedRelative, generateIntlContext} from 'react-intl';
139+
import {IntlProvider, FormattedRelative, createIntl} from 'react-intl';
140140
import RelativeDate from '../relative-date';
141141

142142
expect.extend(expectJSX);
@@ -146,7 +146,7 @@ describe('<RelativeDate>', function() {
146146
const renderer = createRenderer();
147147
const date = new Date();
148148

149-
const intl = generateIntlContext({
149+
const intl = createIntl({
150150
locale: 'en',
151151
defaultLocale: 'en',
152152
});
@@ -252,13 +252,13 @@ Testing with Enzyme works in a similar fashion as written above. Your `mount()`e
252252
*/
253253

254254
import React from 'react';
255-
import {IntlProvider, generateIntlContext} from 'react-intl';
255+
import {IntlProvider, createIntl} from 'react-intl';
256256
import {mount, shallow} from 'enzyme';
257257

258258
// You can pass your messages to the IntlProvider. Optional: remove if unneeded.
259259
const messages = require('../locales/en'); // en.json
260260

261-
const intl = generateIntlContext({
261+
const intl = createIntl({
262262
locale: 'en',
263263
defaultLocale: 'en',
264264
messages,

docs/Upgrade-Guide.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- [Jest](#jest)
1717
- [webpack babel-loader](#webpack-babel-loader)
1818
- [Apostrophe Escape](#apostrophe-escape)
19+
- [Creating intl without using Provider](#creating-intl-without-using-provider)
1920

2021
<!-- tocstop -->
2122

@@ -32,7 +33,8 @@
3233

3334
- `FormattedRelative` has been renamed to `FormattedRelativeTime` and its API has changed significantly. See [FormattedRelativeTime](#formattedrelativetime) for more details.
3435
- `formatRelative` has been renamed to `formatRelativeTime` and its API has changed significantly. See [FormattedRelativeTime](#formattedrelativetime) for more details.
35-
- Escape character has been changed to apostrophe (`'`). See [Apostrophe Escape](#apostrophe-escape) for more details
36+
- Escape character has been changed to apostrophe (`'`). See [Apostrophe Escape](#apostrophe-escape) for more details.
37+
- `IntlProvider` no longer inherits from upstream `IntlProvider`.
3638

3739
## Use React 16.3 and upwards
3840

@@ -350,3 +352,28 @@ Previously while we were using ICU message format syntax, our escape char was ba
350352
```
351353

352354
We highly recommend reading the spec to learn more about how quote/escaping works [here](http://userguide.icu-project.org/formatparse/messages) under **Quoting/Escaping** section.
355+
356+
## Creating intl without using Provider
357+
358+
We've added a new API called `createIntl` that allows you to create an `IntlShape` object without using `Provider`. This allows you to format things outside of React lifecycle while reusing the same `intl` object. For example:
359+
360+
```tsx
361+
import {createIntl, createIntlCache, RawIntlProvider} from 'react-intl'
362+
363+
// This is optional but highly recommended
364+
// since it prevents memory leak
365+
const cache = createIntlCache()
366+
367+
const intl = createIntl({
368+
locale: 'fr-FR',
369+
messages: {}
370+
}, cache)
371+
372+
// Call imperatively
373+
intl.formatNumber(20)
374+
375+
// Pass it to IntlProvider
376+
<RawIntlProvider value={intl}>{foo}</RawIntlProvider>
377+
```
378+
379+
This is especially beneficial in SSR where you can reuse the same `intl` object across requests.

src/components/message.tsx

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {formatMessage as baseFormatMessage} from '../format';
1313
import {
1414
invariantIntlContext,
1515
DEFAULT_INTL_CONFIG,
16-
createDefaultFormatters,
16+
createFormatters,
1717
} from '../utils';
1818
import {PrimitiveType, FormatXMLElementFn} from 'intl-messageformat/core';
1919

@@ -35,7 +35,7 @@ const defaultFormatMessage = (
3535
...DEFAULT_INTL_CONFIG,
3636
locale: 'en',
3737
},
38-
createDefaultFormatters(),
38+
createFormatters(),
3939
descriptor,
4040
values as any
4141
);
@@ -67,22 +67,12 @@ export class BaseFormattedMessage<
6767
}
6868

6969
shouldComponentUpdate(nextProps: Props<V>) {
70-
const {values} = this.props;
71-
const {values: nextValues} = nextProps;
72-
73-
if (!shallowEquals(nextValues, values)) {
74-
return true;
75-
}
76-
77-
// Since `values` has already been checked, we know they're not
78-
// different, so the current `values` are carried over so the shallow
79-
// equals comparison on the other props isn't affected by the `values`.
80-
let nextPropsToCheck = {
81-
...nextProps,
82-
values,
83-
};
84-
85-
return !shallowEquals(this.props, nextPropsToCheck);
70+
const {values, ...otherProps} = this.props;
71+
const {values: nextValues, ...nextOtherProps} = nextProps;
72+
return (
73+
!shallowEquals(nextValues, values) ||
74+
!shallowEquals(otherProps, nextOtherProps)
75+
);
8676
}
8777

8878
render() {

0 commit comments

Comments
 (0)