Skip to content

Commit

Permalink
feat: Add I18n render prop component (#282)
Browse files Browse the repository at this point in the history
* refactor: remove lambda component from withI18n hoc

* docs: document I18n render prop component
  • Loading branch information
tricoder42 authored Aug 24, 2018
1 parent cc57a2d commit 3804fa8
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 9 deletions.
59 changes: 59 additions & 0 deletions docs/ref/react.rst
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,65 @@ top-level application component. However, if the ``language`` is stored in a
);
}
I18n
----

.. component:: I18n

:param bool update: Subscribe to catalog and activate language updates

:component:`I18n` injects ``i18n`` object and ``i18nHash`` to child component, which
may be lambda component, regular component or React element. This pattern is
known as `render prop component <https://reactjs.org/docs/render-props.html>`_.

If want to use ``i18n`` object in instance or lifecycle methods, consider using
:js:func:`withI18n` high-order component.

``i18nHash`` is useful when rendering pure components or elements as it contains
hash of active language and catalogs. Instead of comparing ``i18n`` object it's
enough to compare ``i18nHash`` to decide if component should update.

Using lambda components:

.. code-block:: jsx
import React from "react"
import { I18n } from "@lingui/react"
function LogoutIcon = () => (
return (
<I18n>
{({ i18n }) => <Icon name="turn-off" aria-label={i18n.t`Log out`} />}
</I18n>
)
)
Using components and elements:

.. code-block:: jsx
import React from "react"
import { I18n } from "@lingui/react"
function TranslatedComponent({ i18n }) {
return <Icon name="turn-off" aria-label={i18n.t`Log out`} />
}
function RenderingElements = () => (
return (
<I18n>
<TranslatedComponent />
</I18n>
)
)
function RenderingComponents = () => (
return (
<I18n>{TranslatedComponent}</I18n>
)
)
withI18n
--------

Expand Down
1 change: 0 additions & 1 deletion packages/react/src/I18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ export default class I18n extends React.Component<*, *> {
static defaultProps = {
update: true,
withHash: true,
withRef: false
}

static contextTypes = {
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/withI18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const withI18n = (options: withI18nOptions = {}) =>
}
return (
<I18n update={update} withHash={withHash}>
{i18nProps => <WrappedComponent {...props} {...i18nProps} />}
<WrappedComponent {...props} />
</I18n>
)
}
Expand Down
19 changes: 12 additions & 7 deletions packages/react/src/withI18n.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ describe("withI18n", function() {
const spy = jest.fn()
const Sink = withI18n(options)(
class Sink extends React.Component<*> {
customMethod = () => 42

render() {
spy(this.props)
return <div />
Expand Down Expand Up @@ -154,11 +152,18 @@ describe("withI18n", function() {
})

it("should hold ref to wrapped instance when withRef is enabled", function() {
const { node } = mountHoc({}, { withRef: true })
const hoc = node.instance()
expect(hoc.getWrappedInstance()).not.toBeNull()
expect(hoc.getWrappedInstance().customMethod).not.toBeNull()
expect(hoc.getWrappedInstance().customMethod()).toEqual(42)
class Component extends React.Component<*> {
customMethod = () => 42
render() {
return null
}
}
const WrappedComponent = withI18n({ withRef: true })(Component)

const node = mount(<WrappedComponent />).instance()
expect(node.getWrappedInstance()).not.toBeNull()
expect(node.getWrappedInstance().customMethod).toBeDefined()
expect(node.getWrappedInstance().customMethod()).toEqual(42)
})

it("should not hold ref to wrapped instance when withRef is disabled", function() {
Expand Down

0 comments on commit 3804fa8

Please sign in to comment.