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
Refactor nested example to move <IntlProvider> into widget #235
Conversation
If we do this, the Widget should also take a |
You're right. Fixed it! |
@baer curious to hear more about what's leading you towards putting the nested |
Well, it all boils down to this: I write widgets every day that need to be consumed by an wide variety of consumers. They generally fall into 3 categories and while I tried to be specific about our needs in the descriptions I think these categories apply generally to lots of companies/projects. It's just easier to give concretes examples than hypotheticals :)
It also IMHO indicates a a leaky abstraction to have to ask a widget's consumer to take a dependency on an i18n lib since it's an implementation detail. For Consumers 2 and 3, I've chosen to build widgets with their own |
For consumers 2 it still leaks out that React Intl is being used because they'll have to load React Intl's locale data for the locale they want to render in. Makes sense for 3 though, assuming English-only. |
Re: Consumer 2 I didn't think of that... The answer for me might be that all widgets include all locales and I do some pruning in my build step. Without having the consumer register locale data I'm not sure how to handle that. Moment.js has given this a lot of thought and their solution is erm... uhhh... not great :). |
Also, you likely don't want end up with multiple copies of a library like React Intl, so some widget developers might include it in |
In npm@3 you shouldn't have multiple copies eve if it's not a peerDependency. Maybe you've thought of something I haven't though. Can you elaborate on that? |
In our internal "Widget" setup we're putting React Intl in Consumer 2 seems to be the most interesting… that's why I was curious to hear more about how you're handling that case. I'm on board with pulling this in this PR, maybe I'll make the example show both forms ( |
Some details on the Consumer 2 case. Actually, since writing Consumer 2 is going to get annoying I'm going to call them External Integrators. I'll speak in detail about our (Walmart) use case but I believe that many of these things will be true in a more general setting. Walmart like Facebook and Microsoft, has a wide range of business units with teams that work mostly independently and are each responsible for their own technical decisions / stack. This allows for a somewhat darwinian approach to development and tooling where the truly good things tend to spill over into other teams. Usually when one team latches onto something excellent they try to spread the good word. This is actually how React came to be at Facebook (but I'm sure you already knew that). The reason I mention all of this is that every interaction with other teams (External Consumers) expends some amount of political capital which means that in order to be successful the new tools have to be demonstrably better than what they had and have a low barrier to entry. The more I ask these consumers to change their build process, add dependencies etc. the harder the sell is and the slower the rollout. This is a stripped down version of how one of my Widgets looks: components/greeting.jsx "use strict";
import React from "react";
import {IntlProvider, FormattedMessage, defineMessages} from 'react-intl';
// All messages MUST be defined in a defineMessages block. Any messages defined
// elsewhere will throw errors from a custom ESLint plugin. Having 1 way to do things
// is very important since contributors come from many teams both in the US and
// abroad. Communicating simple rules is paramount to making tooling effective.
const defaultMessages = defineMessages({
"greeting": {
id: "greeting",
description: "A friendly greeting for a user visiting the homepage",
defaultMessage: "Hello There!"
}
});
export default React.createClass({
propTypes: {
messages: PropTypes.object,
locale: PropTypes.string
},
render() {
// All components that are exposed through (require("package.json")).main MUST be
// wrapped in an IntlProvider. This ensures the component is useful for Consumers
// 2 and 3 discussed above
return (
<IntlProvider messages={this.props.messages}>
<FormattedMessage {...defaultMessages["greeting"]}/>
</IntlProvider>
);
}
}); index.js module.exports = {
Greeting: require("./dist/components/greeting"),
// This is the flattened result of running the `babel-plugin-react-intl` All keys are
// prefixed with a SHA hash (a babel plugin I just wrote) to prevent collisions in
// an External Consumer's messages file since I have to assume they will want
// to roll messages up into a single bundle in their build process.
messages: require("dist/messages-raw.json")
}; I hope this gives you a better idea of what I'm trying to accomplish. Let me know if you have any questions. |
@baer yeah it is! How are you documented that the React Intl locale data must be loaded on the page, along with the |
Honestly, I don't have good answers to those questions yet. It seems like all bad options so I was kindof punting on the question for now 😕. Polyfill
locale-data
If you have any ideas I'm all ears :). Also, thank you again for all the amazing work you do. Your responses are always really thoughtful and on point and the work you've been putting out is exceptional. I just wrote my first babel-plugin and it's a freaking abyss filled with missing documentation. You're killin' it with |
Thanks for these kinds words! 😄 I'm literally working on the exact same problem within Yahoo. But we're much more in the consumer 1 use case, so it's easier for our framework to provide the nested The consumer 3 story is slightly easier (assuming English-only), because they'd just need to load the |
Sent a PR against your branch: https://github.com/baer/react-intl/pull/1 |
I'd like to do two things, if you don't mind:
This is the setup: components/some-widget.js
And, somewhere up in the hierarchy: components/some-app-route.js
and finally, at the very top (presumably): index.js
It seems to me that this is a proper usage aside from the line above in
Would you say this is correct? And that this is the intended usage of the nested Thank you. |
@baer Just reviewing old PRs, do you still want to pursue this or should we close it? |
Probably time to close it out. |
Refactor the example to move the
<IntlProvider>
inside of the widget. Without this, the widget will be unusable as a standalone component.Note: This is best viewed with the whitespace flag. Indenting made the diff look much bigger than it is.