1

it's the first time I use react-intl with Mobx state tree.

Basically I have two buttons in the header (it and en) that on click set the current language saved in a Mobx State Tree variable.

Something like this.

In my state:

const FULL_LANGUAGE =
  (navigator.languages && navigator.languages[0]) || navigator.language || 'en-GB'
const LANGUAGE = FULL_LANGUAGE!.split('-')[0]

export const UIModel = t
  .model('UIModel', {
    language: LANGUAGE,
  })
  .actions((self) => ({
    setLanguage(language: 'it' | 'en') {
      self.language = language
    }
  }))

In the header component:

...
<Button textToShow="it" onClick={() => setLanguage('it')} />
<Button textToShow="en" onClick={() => setLanguage('en')} />
...

my index.tsx file:

import React from 'react'
import ReactDOM from 'react-dom'
import { IntlProvider } from 'react-intl'
import { stateInstance, Provider } from './state'
import { App } from './App'
import messagesIt from './locales/messages-it.json5'
import messagesEn from './locales/messages-en.json5'

const language = stateInstance.ui.language
const messages = language === 'en' ? messagesEn : messagesIt

function renderApp() {
  ReactDOM.render(
    <Provider value={stateInstance}>
      <IntlProvider locale={language} messages={messages}>
        <App />
      </IntlProvider>
    </Provider>,
    document.getElementById('root')
  )
}

renderApp()

and my Mobx State Tree root file:

import { createContext, useContext } from 'react'
import { Instance, types as t } from 'mobx-state-tree'
import { UIModel } from './UI'

export const StateModel = t
  .model('StateModel', {
    ui: t.optional(UIModel, {}),
  })

export const stateInstance = StateModel.create()
export interface StateInstance extends Instance<typeof StateModel> {}

const RootStateContext = createContext<StateInstance | null>(null)
export const Provider = RootStateContext.Provider

export function useMst() {
  const state = useContext(RootStateContext)
  if (state === null) throw new Error('')
  return state
}

The problem is that when I click on it or en button, the language in the state is updated but nothing change I think because <IntlProvider locale={language} messages={messages}> is not "rerendered".

How can I solve?

Danila
  • 15,606
  • 2
  • 35
  • 67
whitecircle
  • 237
  • 9
  • 34

2 Answers2

1

Lots of ways to solve it, for example I think you can wrap IntlProvider in Observer (docs) component to make it reactive:

import { Observer } from 'mobx-react';

// ...

function renderApp() {
  ReactDOM.render(
    <Provider value={stateInstance}>
      <Observer>
        {() => (
          <IntlProvider
            locale={stateInstance.ui.language}
            messages={
              stateInstance.ui.language === 'en' ? messagesEn : messagesIt
            }
          >
            <App />
          </IntlProvider>
        )}
      </Observer>
    </Provider>,
    document.getElementById('root')
  );
}
Danila
  • 15,606
  • 2
  • 35
  • 67
0

Solved in this way:

const AppContainer: React.FC<{}> = observer(({}) => {
  const {
    ui: { language },
  } = useMst()
  return (
    <IntlProvider locale={language} messages={language === 'en' ? messagesEn : messagesIt}>
      <App />
    </IntlProvider>
  )
})

function renderApp() {
  ReactDOM.render(
    <Provider value={stateInstance}>
      <AppContainer />
    </Provider>,
    document.getElementById('root')
  )
}
whitecircle
  • 237
  • 9
  • 34
  • Yep, also nice solution! Please mark some answer as accepted if the question is solved, thank you. – Danila May 31 '21 at 17:02