Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

example of dynamically changing the definiotn #26

Closed
OrenMe opened this issue Sep 5, 2018 · 5 comments
Closed

example of dynamically changing the definiotn #26

OrenMe opened this issue Sep 5, 2018 · 5 comments

Comments

@OrenMe
Copy link

OrenMe commented Sep 5, 2018

Is this possible? can you add a sample of how to dynamically change the definition and thus causing a refresh of the text nodes in all of the children.

thanks.

@billneff79
Copy link
Contributor

If you use the IntlProvider component to provide the definition file, anytime you (re)render the IntlProvider it will (re)render its tree, which will update any child components with the new definitions. Thus, in order to have dynamic definitions on the screen, you just need the definition prop to IntlProvider to itself be dynamic. For example, here is a trivial example that would alternate "Hello" and "Ciao" on the screen every second:

const English = {hello: 'Hello'};
const Italian = {hello: "Ciao"};

class App extends Component {
  state = {
    definition: English
  }

  componentDidMount() {
    setInterval(() => this.setState({definition: this.state.definition === English ? Italian : English}), 1000);
  }
  
  render(props, {definition}) {
    return (
      <IntlProvider definition={definition}>
        <Text id="hello" />
      </IntlProvider>
    );
  }
}

In a more real-world scenario, you would get the desired language file "key" from something like a users preferences or the browsers language preference, dynamically lazy-load/require the language file, and update state appropriately with the new language file, triggering a rerender. Something like this:

import defaultLanguage from '../languages/en-us.json';

class App extends Component {

  state = { definition: defaultLanguage };

  componentWillMount() {
    let key = ??? //get preferred language file key somehow

    //use webpack dynamic import if you have all of your language files at build time - could also use fetch or another mechanism to get a remote file if you don't have them at build time
    import(`../languages/${key}.json`).then(definition => this.setState({definition}));
  }  

  render(props, {definition}) {
      <IntlProvider definition={definition}>
        <Text id="hello" />
      </IntlProvider>
  }

}

See more about webpack dynamic imports here: https://webpack.js.org/guides/code-splitting/#dynamic-imports

@billneff79
Copy link
Contributor

This would be good to add to the README. If anyone wants to clean it up and update it, that would be helpful

@OrenMe
Copy link
Author

OrenMe commented Sep 9, 2018

thanks @billneff79 , great example.
Although, this is a bit of simplistic example.
In my case I have complex tree structure that doesn't get re-rendered all the time.
Currently I have one top-level <IntlProvider /> for my entire app, which worked OK for initial one time init.
Should I use multiple <IntlProvider /> so parts which dynamically get's re-rendered will use their own scoped provider?

@billneff79
Copy link
Contributor

billneff79 commented Sep 10, 2018

@OrenMe - you can have nested <IntlProvider /> components at different levels in the tree. The current behavior of that nesting is that it is purely additive: if a higher-up IntlProvider has key a with value foo and a lower IntlProvider has key a with value bar, then the resulting merge will have key a with value foo. If the lower IntlProvider has keys that the higher IntlProvider does not have, then they will be added to the merged definition for components to use.

The typical pattern that we use is that each widget we export from a repo is wrapped in an IntlProvider that provides the default language in that repo. That makes it easy to test the base functionality of a widget. We then have an Application level repo that imports all of the necessary named widgets and it is itself wrapped in an IntlProvider. It does not provide anything for our default language, as the base widgets already have that, but it provides all language updates for every widget in alternative languages (e.g Spanish). Any components that selective render through something like shouldComponentUpdate take account for updates to the language definition file - it's so rare that it's not a big burden.

An alternative you could pursue is a publish-subscribe mechanism (e.g. using mitt for something simple or redux for something more complex if you already have that in your codebase) to reach deep into the your tree structure to inform certain components that they need to re-render because of intl language file changes.

Either way, if you find something that works for you, we'd love to hear about it. Good luck!

@OrenMe
Copy link
Author

OrenMe commented Sep 12, 2018

thanks @billneff79 !

@OrenMe OrenMe closed this as completed Sep 12, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

2 participants