-
Notifications
You must be signed in to change notification settings - Fork 108
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
RFC: React Cross-Platform Strategy #54
Comments
Hey, thanks for sharing. This sounds good. Internally I've experimented with aliasing to |
I'm concerned about relying on flexbox for this cross-platform effort, because that tightly constrains the lower bound of a supported browser list in a way that will prevent many companies and users, including Airbnb, from adopting it. While it's possible that in a few years, a lack of flexbox support won't be a problem, it is a problem now, and past history with browser versions suggests that we shouldn't count on that problem going away any time soon. |
Fair enough. That's not really a problem for Twitter – I think the browser support matrix for both twitter.com and mobile.twitter.com includes only flexbox-supporting browsers. |
@ljharb I believe the problem of browser support is what @lelandrichardson was referring to when he said
I don't know if we know how fast that implementation would be, the size, etc...but I feel like it'd be worth a try to get a decent polyfill across ie8/9/etc |
Also, evidently this emscripten'd Yoga implementation now exists as of 2 days ago: https://github.com/facebook/yoga/tree/master/javascript |
If we had a reliable and performant way to fully polyfill/transpile/compile a subset of flexbox (and easily prohibit/lint against whatever's not polyfilled) down to all ES5 browsers, that'd be plenty good enough I think. If we can include any ES3 browsers (like IE 8) I think we've got an unstoppable juggernaut of standardization going. |
Awesome writeup @lelandrichardson! These tenets are critical pieces of the dream of making one app work effortlessly on any platform. I would encourage you to emphasize one more tenet: feature detection over platform gating. In a world with feature detection, we can encourage competition in the platform ecosystem. Take TouchID encryption, for a random example: if I assume that it is forever an iOS-only feature, I should put it behind a platform gate. But if we want other platforms to implement that awesome functionality, I would put it behind a feature check. Then Microsoft and Google can compete to implement it and get better-featured apps for their stores. (Plus, you'd need to implement that check anyways in order to properly support older iOS devices.) I also think we should always support platform checks as an escape hatch. Just like how we allow escaping to native code in RN, there are tons of real world cases where we need to let the developers drop down and handle something in the DOM when they need to. We could stimulate a ton of competition in the ecosystem: from camera features and other sensors, to GPU optimizations, home automation, VR, and native/OS navigation. If we want to live in a world where our portable code just works in more places, I think we should emphasize feature detection. |
Interesting. This is a good point, and I haven't given it a lot of thought. This is kind of inline with something that @vjeux wrote up a while ago with deciding the right interfaces for RN. I can't find it right now, but the main point of it was we should try to match the web standard's specs first (ie, I'm not sure I fully understand how these principals apply to things like Touch ID which aren't web APIs. I know you just picked it as an example, but I'm not sure what the right way to share that would be. How do you envision "encourage feature detection" being pulled into this proposal? Are you envisioning this as something instead of the Platform API? |
I specifically picked that example because it isn't a web API yet. Basically I'm suggesting that the JS app should check for presence of native features and opts in to them when available. Instead of specifying behavior for each platform, an app would specify behavior for cases where different native features are available. Another practical example may be the camera. If I adopt a camera library in my app, I should check with the library to determine if there is a camera on the device, rather than assuming certain platforms have cameras. And I should check with the library to see if the camera has a LED, to determine if the flash buttons should appear. The standards web API may not support flash settings, but if my app checks with the library, then the library can later add LED support for new environments, even though the standards support lags behind.
I think the platform API should be available as an escape hatch for when you absolutely need to override based on the platform. This should be used when the product requirements are different, designs are different, or in cases where probing the native modules for feature detection causes instability. Maybe the three tenets can be reworked as such:
|
As I'm thinking about this more.... what does "Feature Detection" look like that doesn't work through globals? Typically feature detection is something like "does this thing exist" or "if i do this thing, does this result look right". The problem is I don't know how this maps to user-land modules? How do I say "If "react-platform-fancy-button" works for me, then use it. Otherwise use this"? |
You make a good point- it would be nice if we had a standard for safe feature detection. As you point out, only standard-ish way of doing it right now is checking for the presence of a global. |
First of all, I wanna say that this is AWESOME*! I really love where this is going. The better we can all pool our individual efforts, the better the overall ecosystem will be. As I start digging into the nitty gritty, a couple of things come up, however:
I'm confused why this isn't a primitive. Is it because of view-only Sketch? IMO all UIs boil down to viewing info, interacting with info and modifying info. How awesome would it be if
Have we given any thought about where semantic markup goes in this world. I'm guessing And where do As I see things, in order to build a realistic web app, you would end up having to use "web native" tags in order to build for SEO and A11Y. And as a result the need to build versions for each platform moves higher and higher upwards. The only thing I can think of is that there is another layer on top of the primitives for things like Does that make sense? |
Perhaps React Native can start by moving the platform-specific APIs and components out of the main library's export. These problems are harder once the RN ecosystem is built around the assumption that there are only 2 platforms, forcing other platforms to stub or implement iOS/Android modules like
Agreed. I think it should be; it's part of the "core" exports we need and use on mobile.twitter.com.
This is factored into react-native-web.
If you have any ideas on how to improve this, I'm interested to hear them. |
Agreed all around! Thanks for the response. So if the primitives come with an I first read So for a component like |
It would probably feel less weird if React Native had been called something like React Platform:
Well, there would only be one The long-term objective / hypothesis is that we'd use the various escape hatches as infrequently as possible, for low-level building blocks, so that the rest of the ecosystem above is built on a unified, platform-agnostic, JavaScript-only API. But at the moment even RN has a lot of iOS/Android-specific deviations exposed in the APIs of its core components and features :) |
First off! Amazing work! This is so great! 🙌 Privately, at our company we've been building cross platform components on top of @necolas @lelandrichardson are you guys still planning on moving forward with FWIW I think @ericvicenti's core tenet revisions are pretty bang on.
I'm also not sure how you do feature detection other than having a mapping for each supported platform much like do with Which leads me to another question. Is the goal of
Personally, I think that it probably makes sense to also have a small set of common APIs. If that becomes the case I see some parallels with ReactXP. Although IMHO I think what they have done could probably be broken down into 2 layers as some of the components seem to be a bit more meta. |
I'm late. This issue excites me. I wanna to know any progress about the |
@mathieudutour @lelandrichardson and @necolas I've been doing some work recently that involves additional platforms, and I'm moving toward standardizing around primitives, Webpack, and Lerna for React Native development. I heard there are some plans within the RN project itself around creating an abstraction, "primitives", and platform-support. I agree with the original post as well as the need for
We're looking at TextInput and names in Sketch to provide metadata and operations within Sketch, so I think it may warrant trying to find a platform that doesn't have Regardless, is Thanks in advance, and thank you all for your incredible work! |
@jhampton I would love to see a repo with a basic setup, if you have one - I was trying to work with Primitives recently (using webpack-blocks for build) and running into trouble. Sorry for being a bit off-topic. |
@adamcee Thanks for mentioning |
Are you currently developing this on a repo we can contribute? |
It looks like Lona is the sibling project in making x-platform possible. I’m investigating that ATM. |
A related discussion has come up recently as we have been working on Fabric for React Native. With Fabric, the native definitions of props are moving to C++ and platforms like iOS and Android will implement that interface. That means we are getting to a set of primitives that are the common denominator across multiple platforms and trying to figure out how platforms can extend that with custom functionality. I wrote up a bunch of stuff over in the RN proposals repo and would like some eyes on it from people who’ve participated in this React-primitives conversation in the past. I think we are starting to move in this direction. react-native-community/discussions-and-proposals#50 (comment) |
Hi, I've implemented a proof-of-concept/experimental repository based on some of the ideas discussed in this RFC, as a community-led/open-source project, to help push forward progress, as it's been a while since these proposals have been made. I've created a My repo builds on top of the work that I've been doing lately with The plan is for I think one of the coolest applications of the repo, is having packages like I'm planning to develop cross-platform wrappers for SVG, Lottie, date pickers, etc for it too. This can already be used today to help make React Native apps be able to render to Would love your thoughts @lelandrichardson @necolas @mathieudutour . And should this discussion/proposal be migrated over to react-native-community/discussions-and-proposals#50 ? |
to: @necolas
This is mostly in response to #31, but also just something I've been thinking about for some time, and is a proposal for a path to building a real production-quality ecosystem for building UI with React across multiple platforms in a single code base.
At the moment this isn't very easy, and there's no standard or agreed upon way to do it. React Native has been the closest thing to accomplishing this, with the two platforms being iOS and Android platforms (and others in the community sprouting up, such as windows, vr, etc.). Projects like react-primitives and react-native-web are attempting to solve this.
I think it's important to come at this with the angle of it not being just "React Native, but running in a web browser". Similarly, I think it's important to come at this thinking more than just "web" and "native", even if we don't know what all of the additional platforms will be. (Though we can make some educated guesses... Windows, VR, desktop, etc.)
I believe there are three main tenets that are needed to accomplish this:
Three Main Tenets
Tenet 1: Platform Extensions
Platform extensions, such as the ones allowed by the React Native Packager, are crucial to creating cross-platform code-bases. It allows for people to create consistent interfaces with different implementations, and make that transparent to the end user.
I believe we should take this convention a step further by creating a webpack resolver plugin that allows for the exact same resolution rules. Additional plugins for other bundlers such as
rollup
andbrowserify
would also be ideal.In addition, I'd like to standardize on a module-scoped package.json directive:
platformCascade
In an NPM module's package.json, a library author would be able to describe the proper cascade of platform extensions, and it would be scoped to every file inside of the package, but not in any dependencies. An example
platformCascade
could be defined in this way:This allows library authors to define cascades that may not be mainstream (i.e., "sketch"), without it having to be standardized (and without causing unknown side-effects in other modules if that extension just happened to be there).
It is critical that this convention works outside of just the RN packager, as it will be required for this ecosystem to extend beyond just RN and the associated tooling.
Tenet 2: Primitives
In order for cross-platform to really work, we need to make a world where if someone wants to build a cross-platform UI component, they don't have to implement it n times for n platforms. This requires at least a few building block components.
We want to be careful in choosing these "primitives". We need enough to be able to build bigger more complex things, but need to keep it minimal enough so that it's reasonable to expect a new platform to be able to implement the full set of them. I believe things like
TextInput
may be really important in terms of some platforms, but not others, and so should maybe not be a primitive. This will have to be defined over time, but I think being conservative at first is ideal.To be honest, I'm actually not sure if I'm even confident about the primitives listed above. For instance, if we wanted to add a platform like "console" and create something like
react-curses
, it's not clear ifImage
orTouchable
actually make sense. These components were mainly chosen because after creating a fairly complete Component Library for Airbnb in React Native, I noticed that almost 95% of it could be implemented with just these primitives. The four that seem really clear to me areStyleSheet
,View
,Text
, andPlatform
, but it's a balance.Tenet 3: Platform built on Primitives
With the "primitives" now built, now it is our job to figure out what useful crap we can build with them! The idea behind the primitives is that they are the building blocks that other more useful things can be built. Perhaps mediocre versions of things that are general and purely implemented by primitives.
Some components will need to have custom platform-specific implementations that will ensure that they perform as good as they possibly can, but we must always have at least one implementation that only uses the primitives (or other higher-order components that themselves have a primitives-only implementation).
There's some obvious candidates for these components. Since we want to be able to share code between React Native and Web, most of the non-platform-specific APIs exported by React Native make obvious choices, but there are obviously lots more.
Proposal
I haven't really proposed anything yet, as much as I've just talked about what I think is important. Here comes the proposal. Please keep in mind that I'm hoping this just starts the conversation. More than anything I'm interested in feedback and seeing if my ideas align with yours.
At the end of the day, I think there'd be a lot of value in us working together, and this is one way I could see that happening while (i hope) serving each of our slightly different use-cases.
The first proposal would be to move/rename
necolas/react-native-web
toreact-community/react-platform
. Alternatively, we could deprecate it and start fresh.react-platform
would become a lerna monorepo. One of the packages in the repo would bereact-primitives
which would include the primitives mentioned above, with the optimal implementation (whether that's RNW's current implementation, or react-primitive's, or some combination of both). Thelelandrichardson/react-primitives
repo would also be deprecated.Various modules that have been written in
react-native-web
would be refactored to be their own package in the lerna monorepo. For instance,ScrollView
andListView
would becomereact-platform-scrollview
andreact-platform-listview
.A convention I could see working for this monorepo is a folder structure like this:
We could also export a "grab-bag" package which could just be
react-platform
, which essentially be whatreact-native-web
currently exports (something that attempts to be API-compatible with RN). Of course, you could also keepreact-native-web
and just depend on all of the correspondingreact-platform-*
packages or whatever was needed and export them directly (which may actually make more sense?).Questions & Answers
Of course the whole point of this is for any library authors to also be able to publish cross-platform components/modules by only depending on
react-primitives
and other fully cross-plat dependencies. So packages don't need to be part of thereact-platform
monorepo in order to be a primitive.My thinking is that by something being in the monorepo, we are asserting that no matter what platform you are on (i.e., could be something obscure like "sketch"), we are guaranteeing that there will be at least one primitives-only implementation (functional, even if it's not optimal). Of course we will also have more optimized implementations in the case of the more popular platforms (namely, "native" and "web").
I'm still not 100% sure what the right answer here is. If we lived in a world where there were ~15 platforms and there was source code for each in this monorepo, the maintenance burden could start to get out of hand. On the other hand, the idea should be that a new platform need only implement the primitives, and everything else should "just work" (even if using the non-optimal cross-platform implementations). My feeling is that the criteria ought to be something like:
No, but it doesn't hurt if we follow an already existing API, and React Native's make the most sense, as most of them have already been built with cross-platform interfaces in mind.
Happo is a CI tool built to detect visual regressions by taking screenshots and diffing them. I've been working on getting happo working for iOS, Android, and Sketch as well.
I believe we should be able to create a bunch of minimal examples with the primitives stressing all of the corner cases of layout as well as styling (box shadows, borders, border radius, transforms, etc.).
We then get two things out of this:
This, in addition to standard stuff like tests, listing, etc. I think will give us a pretty good story in terms of maintenance.
Long Term
React Native
Although this wouldn't have to be an initial goal, I could eventually see implementations of various react native APIs moved into the react-platform repo instead of the react-native one. We have already talked about making RN core more modular and moving it into a similar repo structure. It might make sense for some of these things (like ListView, for example) to live outside of ReactNative as a lot of their optimizations are JS-level optimizations and could benefit all platforms.
Flexbox / Yoga
I know that Sebastian Markbage has been thinking about integrating layout into react for a long time. I think this would bode very well for the concept of "primitives" and also ensure that different platforms would be more consistent. The main thing each platform would then have to provide would be a text measurement API. I'd like to add an emscripten pipeline to Yogo so that there is an optimized and consistent JS implementation of flex box that matches the native implementations. You could also see Web Assembly as a target for Yoga in the long term as well.
Adoption
Imagine if today's most popular
react-*
andreact-native-*
libraries depended onreact-primitives
orreact-platform-*
packages instead ofreact-native
or "host" DOM components like<div>
and<span>
. Entirely new platforms could be born overnight by just implementing the primitives, and entire code bases could be made to target those platforms with just the changing the "platform" target of your JS bundler, and potentially implementing a couple of platform-specific interfaces where needed.I look forward to your thoughts, and hope that we can find a time to talk about this more seriously if you are interested (perhaps next React Wednesday?)
The text was updated successfully, but these errors were encountered: