question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

React Hooks support

See original GitHub issue

I’d like to start a discussion about react hooks in lingui-react. I know that at this moment is just in alpha, but maybe is time to start thinking about a better alternative of i18n renderprops.

IMO, I don’t like this alternative:

function Example(){
  return (
    <I18n>
        {({ i18n }) => (
           <img src="..." alt={i18n._(t`Image caption`)} />
         )}
    </I18n>
  )
}

The reasons are:

  • So much code to just one thing
  • Renderprops are creating an extra useless wrapper
  • Having a lot of renderprops like this is not so much readable, it’s like callback hell…

A better alternative can be:

function Example(){
  const alt = useT('Image caption')

  return (
     <img src="..." alt={alt} />
  )
}

What do you think about this?

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:12 (5 by maintainers)

github_iconTop GitHub Comments

21reactions
tricoder42commented, Nov 13, 2018

Hey @aralroca, thank you for suggestion. I already mentioned hooks briefly in my talk at React Conf 2018, but let’s give it a proper discussion here.

In upcoming v3 we’re planning to abandon both HOC and render prop component. They both have the same purpose: consume i18n object from context and provide it to child component. Question is (and this question was asked few times, e.g: #46), why can’t we access i18n object directly, like so:

function Example(){
  const alt = i18n._('Image caption')

  return (
     <img src="..." alt={alt} />
  )
}

The only reason is re-rendering - when you change the active language or re-load message catalogs, ideally you would like to re-render your translations. HOC and render prop component take care of this. However, we figure out that in practice this happens rarely and when you switch the locale, it’s safe to re-render whole app.

So, instead of context we’re planning to use global i18n object:

// Lingui v2.x
import { I18n } from "@lingui/react"
import { t } from "@lingui/macro"

function Example(){
  return (
    <I18n>
        {({ i18n }) => (
           <img src="..." alt={i18n._(t`Image caption`)} />
         )}
    </I18n>
  )
}

// Lingui v3.x
import { t } from "@lingui/macro"

function Example(){
  const alt = t`Image caption`

  return (
     <img src="..." alt={alt} />
  )
}

t macro is transformed into i18n._ call, where i18n is imported from @lingui/core. There’s gonna be an escape hatch in macro configuration if you don’t like to use global i18n object (e.g. if you prefer to use your own instance of i18n for whatever reason).

Hooks

To sum up previous section, there won’t be no HOC or render props in next version.

So what about hooks? My first impression was skip them completely, but I think we could provide a single hook, which would trigger re-render. This hook would be used only once in the root of your app like this:

import { useLingui } from "@lingui/react"

function App() {
  const { locale } = useLingui()
  // When locale changes, the whole App re-renders
  return <App key={locale}>...</App>

Everywhere else you would be fine with macros:

import { t, Trans } from "@lingui/macro" 

function Component() {
  const title = t`Header title`
  return <h1 title={title}><Trans>Hello World</Trans></h1>
}

Macros

Transforming macros into i18n._ calls is just one thing. The main purpose is to transform JSX or tagged template literals into ICU Message Format. That includes plurals and date/number formatting:

import { plural } fro "@lingui/macro"

const bottles = plural({
  value: numBottles,
  one: `# bottle is hanging on the wall`,
  other: `# bottles are hanging on the wall`
})

// Under the hood it's the same as
import i18n from "@lingui/core"

const bottles = i18n._(
  "{numBottles, plural, one {# bottle is hanging on the wall} other {# bottles are hanging on the wall}}",
  { numBottles }
)

The same API can be used also in projects without React.

However in JSX it’s more convenient to use components instead, which allows us to use inline components without any limitations:

import { Trans } from "@lingui/macro"

const Link = () => <Trans>Read the <a href="/docs">full story</a>.</Trans>

// Under the hood it's transformed into
import { Trans } from "@lingui/react"

const Link = () => (
  <Trans 
    id="Read the <0>full story</0>." 
    components={[<a href="/docs" />]} 
  />
)

Conclusion

That’s why I don’t want to use hooks for translation:

  • Hooks are only for React, but i18n should work everywhere. Even in React apps you might have a function, which doesn’t have access to React props/context.
  • Hooks would only solve translation (loading message from catalog), but in practice we have to deal with formatting and macros are super useful for that.

On the other hand, I’m open to useLingui hook which returns active locale that could trigger re-render on locale change.

What’s your opinion about all of this?

4reactions
tricoder42commented, Nov 14, 2018

Is there some inconvenient to use it outside the component?

import { t } from "@lingui/macro"

const alt = t`Image caption`

function Example(){
  return (
     <img src="..." alt={alt} />
  )
}

It is, actually! If you use it outside component, then the translation is evaluated only once when the module is loaded. Most probably the translations won’t be loaded at the time.

This is a problem and we still need to figure out how to handle it. More common usecase are translations in default props:

import { t } from "@lingui/macro"

function Example({ alt }){
  return (
     <img src="..." alt={alt} />
  )
}

Example.defaultProps {
  alt: t`Image caption`
}

It’s the same problem. When module is loaded, Image caption is translated, but at that point we might not know the active locale or message catalogs aren’t loaded. Usually this is solved using lazy translations, i.e.: you define that this string should be translated, but it isn’t translated immediately:

import { t } from "@lingui/macro"

function Example({ alt }){
  return (
     <img src="..." alt={alt} />
  )
}

Example.defaultProps {
  // example, the API isn't final yet
  alt: t.lazy`Image caption`
}

// under the hood it's translated in something like this:
// alt: () => i18n._('Image caption')
Read more comments on GitHub >

github_iconTop Results From Across the Web

Introducing Hooks - React
React 16.8.0 is the first release to support Hooks. When upgrading, don't forget to update all packages, including React DOM. React Native has...
Read more >
React Hooks - W3Schools
Hooks allow function components to have access to state and other React features. Because of this, class components are generally no longer needed....
Read more >
useHooks - Easy to understand React Hook recipes
Hooks are a feature in React that allow you use state and other React features without writing classes. This website provides easy to...
Read more >
React Hooks Tutorial – useState, useEffect, and How to ...
Hooks were first introduced in React 16.8. And they're great because they let you use more of React's features – like managing your ......
Read more >
The Guide to Learning React Hooks (Examples & Tutorials)
First released in October of 2018, the React hook APIs provide an alternative to writing class-based components, and offer an alternative approach to...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found