-
Notifications
You must be signed in to change notification settings - Fork 127
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
Unifying the way platforms are handled #50
Comments
Our team definitely wants to enable other platforms to be first class citizens. Another platform that wasn’t included in your list is ReactVR / React360. :-) I think this would likely be much simpler after the slimmening but thinking about what it would take doesn’t need to wait until then. Our team at Facebook knows this is a problem but since we don’t maintain any out of tree platforms we aren’t sure what the pain points are. It would be great if the maintainers of a bunch of these projects could work together to define the problems and what a possible solution would like, being explicit on the technical changes that would need to occur to better support them. Also, how many of those changes that need to occur require Facebook to actually do the work vs Facebook just saying “we agree with your direction and will help land PRs”? |
There have been conversations regarding this since Leland's React Primitive, in a way. And also @matthargett's talk at RNEU was about how React Native allows to target way more platforms. That said, I agree with Eli in the fact that after the slimmening it should be easier, also because there are other conversations ongoing that affect this subject, like #49 And overall, yeah, there is a whole complexity around organisation and management and ownership and maintenance. |
@TheSavior I think it's more of a “we agree with your direction and will help land PRs” that is needed from Facebook, although help on redesigning the way we add platforms would be of great help! @kelset Yes, it's actually partly matt's talk that pushed me to try |
This is something I've wanted since the early days of working on a I know @rozele and @pre10der89 probably have lots to share on this subject. |
I want to talk about how we can sufficiently support out of tree platforms without relying on Haste. As mentioned in the React Native Open Source Roadmap, we are working on migrating internal FB code to use the RN public JS API instead of requiring modules via haste, aka changing this:
to
We are almost done making this change internally and want to keep additional code in the old style from landing by disallowing using Haste to require these files. In doing this, it requires us to also remove Haste from react-native core. For example, One side effect of this, from what I've been told, is that out of tree platforms currently rely on Haste to inject platform specific behavior. For example, In order to get rid of all of this, we need to take a different approach. I'm not exactly sure what this approach should be and I'm hoping that the maintainers of out of tree platforms can help provide that insight. For context, we are having a similar problem right now as we are working on Fabric. Right now, many components have different props on iOS and Android. With fabric, these props are in c++ and shared on both platforms (and other platforms will likely reuse this c++ code). That means that in core, iOS and Android are not able to add additional props for the same component that don't exist on the other. Concretely, this has led us to a path where we have three options in our toolbelt for each prop / component:
I've only thought about this from the perspective of component props and I'm curious what other situations there are that out of tree platforms currently use haste for to implement their functionality. Do the three constraints listed above satisfy the cases for other platforms? What other situations are there that we should be thinking about? How else could they be solved? cc everyone I know working on 3rd party platforms: @rozele, @vincentriemer, @necolas, @empyrical, @acoates-ms, @matthargett, @douglowder. |
We've been having some more conversations internally about this and I wanted to better articulate some of the constraints we have and a proposed approach. Constraints:Flow types must be exact, can't dynamically load different props based on platformSince we will be using Flow Types to generate the native code for each component, the flow types must be strict, and exact. All props defined in the type will generate equivalent code for that prop on native. This is the interface of the component that platforms must implement. Platform specific props shouldn't require changes to coreIf This approach should work the same for out of tree platforms as well as Android and iOSRight now View has a bunch of props that are unique for Android and unique for iOS. This is a similar problem for out of tree platforms and whatever approach we take for other platforms should support Android and iOS becoming consistent with that. Apps that support multiple platforms should only include a single platform's code in the compiled app.When an app supports iOS and Windows, when building the iOS app there shouldn't be any native code or JS code that is specific to Windows, and vice versa. Props for one platform should be errors when passed to another platformToday the props for View are a union of Android and iOS props. If you are building an iOS bundle, it should be a type error to specify a prop that is only defined on Android, and vice versa. Ideally avoid using platform extensions like .android.jsThis approach has always been a non-standard thing that React Native has had. It would be great to not rely on this more and instead find a more generic solution that is more consistent with the rest of the JavaScript ecosystem. Proposed Solution:Since we can't upstream the properties for each platform, and we have to lock down the properties on View so platforms can't hook in and extend the View, platform specific properties need to be defined on a separate, custom component from View. Platforms that need specific properties on View will create a separate native component, potentially named after that platform. For example, AppleTV might create an In places where these custom properties are necessary, product code that uses multiple platforms can switch on which View they render. Something like the following. With Metro's inline imports and platform specific bundles, only the
Thoughts? |
First impressions is that this looks pretty good. A couple of curve balls to consider. First is I suspect View will still end up with too much on it to begin with. The natural instinct given the current state of things would be for view to have all the properties which are supported in both android and ios. This will likely mean that we will start with a base class that has some properties which don't apply to windows, macos etc. -- Not the end of the world, other platforms can certainly choose to ignore certainly properties. And I understand how annoying it would be to add the same property to both AndroidView and IOSView which makes it annoying to use. But something to keep in mind. One thing we have been having discussions around is as you have these shared components that have code to support multiple platforms, are you able to use those components without bringing in all the dependencies of all the platforms. In your example will metro/haul be able to compile that module when targeting AppleTV without having to install react-native-android? I suspect the import would fail even if it isn't being used? We really want to be able to get to the point where you are able to write a react-native-appletv app without having to install the android bits. This is something that is avoided currently by having completely separate *.platform.js files. I'll also bring up the possibility of having a tree of platforms. As we expand the list of platforms we may decide we want to have some shared classes between different platforms. It not entirely clear how you would want to dice it, but you could imagine an apple platform which serves as the base for ios, appletv and macos. And a win platform which is a base of win32 and uwp. I haven't had time to fully digest what that would mean in the proposed world. It is something we have been playing with in the current *.platform.js resolution world. But I do like the idea of getting rid of that system completely, as it would help things like flow/typescript type checking not working correctly with that resolution system currently. But I could imagine having an IOSView inherit from an AppleView which inherits from View. The last big problem that out of tree components have that I would be remise to avoid mentioning is around having to copy/paste the built in components. An example of this would be TextInput. If the entirety of TextInput was render() { <RCTTextInput …props> }, then everything would be well typed and enforced in the fabric model. -- Each platform would just have to implement the native side of RCTTextInput. But instead its full of giant if platform statements that dramatically changes the render logic. This means that to implement TextInput in another platform you have to copy/paste the current textinput.js. Pick one of the render forks (android vs ios), and then implement something. Then forever keep track of any change to the base textinput.js component and duplicate that change in your platforms fork of that file. I recognize that this isn't completely related to the above discussion, but is still a prime issue with most out of tree implementations. |
This might be too crazy/weird/complex, but the new custom resolver support that @arcanis added to enable PNP support might be useful here. There is a new option for
For every module lookup it would do, it calls a node subprocess doing something like this (I forget what the actual arguments of the command are - it could differ from this hypothetical!) node .pnp.js {module doing the requiring} {module to require} Which would print an absolute path to What I am proposing is making a custom resolver that would make a copy of Let's say It would have a file called: And would look like this: export type ExampleViewProps= $ReadOnly<{|
doSomethingCool?: ?boolean,
|}>; And an entry in its package.json like this: {
"rnpm": {
"flow": {
"View": {
"ExampleViewProps": "./lib/ExampleViewProps.js"
}
}
} When import type {ViewStyleProp} from 'StyleSheet';
import type {TVViewProps} from 'TVViewPropTypes';
+++ import type {ExampleViewProps} from 'react-native-example/lib/ExampleViewProps';
// ...
export type ViewProps = $ReadOnly<{|
...DirectEventProps,
...GestureResponderEventProps,
...TouchEventProps,
...AndroidViewProps,
...IOSViewProps,
+++ ...ExampleViewProps, This way, prop types from an addon platform would "just work", and would also be properly removed if you were to uninstall the platform. I think TypeScript has custom resolver support too, so something like this could be done for TS prop types too. But given all the issues that were had with tooling by the haste resolver, it would not surprise me if this was even worse for custom tooling support 😱 But I thought I would put that idea out there. Could also potentially enable other special codemods by other platforms, but should probably by kept to a minimum so things don't get too fragile. |
My early impressions are positive, but I agree with @acoates-ms that a concern would be if a platform wanted to provide a core view/module extension with platform-specific functionality, I'd want to avoid needing to copy-paste the JS implementation. This is less of an issue for |
One of the downsides here seems to be that every platform may need to define a custom "View", even if the majority of them share custom properties. For example renderers for web, windows, macos might all need to add mouse and keyboard events to "View". What determines when features/props are shared across enough platforms for them to be added to the core component API?
More recently there's been an effort to remove platform-specific components and props from the core components. One of my concerns here would be that this could again increase platform-fragmentation of the core components, making it harder to target multiple platforms without increasing the number of platform forks in your app code.
I suppose this could be suggested as the replacement for For example, where "react-platform" exports a singular View and papers over the platform-specific differences (plus forwards any statics, refs, etc).
Allowing you to go back to writing code like this.
And relying on the compiler to be aware of which platforms support which props, so it can remove them from the source code before it gets bundled. |
Yay! Feedback!
Yeah, I could imagine that this is likely to happen. I don't have much of an answer here. Perhaps we are fine with this knowing that it is a place to start and will remove a majority of props from existing for all platforms. We can probably refine more in the future.
What do you mean by "compile"? Do you mean compiling the native code for the component? Like can C# code for windows compile the project without having to have react-native-android installed? If so, yes, I think this is an explicit design goal. On the JS side, if your product code has the platform checks to decide which to render, then I think the bundler would need to have those platforms to be able to bundle. I think this becomes more interesting when you use an npm packages which support some combination of packages and have the platform checks for those platforms. I'd imagine those packages would define a
How does this work today?
This is how I'm thinking about it too. For example, maybe two different companies maintain two different projects, react-native-appletv and react-native-androidtv. These companies should be able to iterate independently from each other and from Facebook. However, if they find that their communities have overlap and a bunch of the concepts and props are the same on both platforms, they could collaborate on a react-native-tv base which defines a shared set of platform components which could be used in appletv and androidtv projects, and those projects could further add their own custom props on top of those. I see this very similarly to how Browser vendors work. They can try out and iterate on APIs and platform features, making whatever changes they want without having buy in from other platforms. Once they realize that what they have is pretty well defined and generic to a broader group, they can propose for those APIs to be upstreamed and adopted by more browsers, requiring collaboration with the other vendors. In this case, for now, Facebook is the top most upstream decision maker, but it doesn't always have to be this way and we'd be set up better for a committee driven approach if we decide that makes sense. @vincentriemer and @acoates-ms:
I think we'll see that almost all of this will go away with Fabric. Those statements are because we don't have unified behavior and Props for that component. Fabric forces us to reconcile the props because they will be in shared C++ code. That should radically simplify the behavior of the component. However, even if the component is much more simplified, if there is any logic in JS, and the component renders @empyrical
Are you referring to moving platform specific components out as part of Lean Core (aka the Slimmening)?
I'd expect Platform specific forks in places where people are doing platform specific checks/behavior. In general for cross platform code though there would be no change. This also means that bundle sizes will be smaller because iOS code won't include the JS code for setting Android or web specific props. Do you have another approach to this or a different tradeoff we should be making within these constraints?
The biggest problem with this is that I don't know how you would typecheck this. You might be able to let it typecheck if you support props that aren't defined, but then there would be no way to guard against spelling mistakes like @mdvacca |
Alternatively, instead of adding a prop to Example, instead of this: <AppleTVView parallax={3} /> There could be a component like: <Parallax value={3} /> One advantage of this approach is that if you never use parallax in your app, it could be optimized out of the bundle easily. Now, if a platform wanted to add a custom style attribute, like |
I'm slowly trying to catch up on this but this is really interesting to me and something I'd like to help with. It seems that there is consensus that React Native should support all environments well. To move on this, I have a few more concrete questions, some of which are already being discussed:
I'd really love to hear answers to these which would help with my understanding of what we can do. For the last one, since it's already being discussed, here are my further questions/thoughts:
|
I've been thinking about the user experience for adding platforms to an existing React Native app. I was going to create a separate issue for the proposal, but it seems relevant here. Today, in
My proposal is that we add a command to the @react-native-community/cli that manages this two-step process for you. E.g.:
Or, more generally:
The
Alternatively, we could give more control over the install process to the platform itself by doing something like:
At some far off point in the future, when the "slim core" work comes to it's full potential and we've separated out platforms, your experience for creating a new app for iOS, Android, Windows, and Linux might look like:
In the near term, however, this would just make platform extensions feel less ad hoc :). I understand that we still have a lot of unknowns still with respect to how core overrides will be managed for platforms. However, this is a "low hanging" forcing function for how we can drive platform extensions towards uniformity of approach and architecture. |
Hi, am adding a summary/rephrasing below of a comment that I made in lelandrichardson/react-primitives#54, for extra visibility and to make sure it doesn't conflict with ongoing React Native efforts: I've created a GitHub monorepo and npm namespace for The main idea is to offer cross-platform React code interoperability as an external wrapper around React Native or out-of-tree platforms. e.g. Another example: And Edit I think that it's worth clarifying also, what counts as a React platform. Is using I'm new to the RFC process and the React Native open-source community, so I hope that I'm not causing any confusion, and hope that it's ok for me to use the |
Introduction
Out of the box, react-native is for building Android and iOS (and TvOS?) apps, so
react-native init
starts us of with anandroid
andios
folder and project. However we have seen more and more effort to see implementations for new platforms (windows, MacOS, web (react-native-web or react-native-dom), desktop alltogether) which shows us that react-native is more than just iOS and android.The Core of It
I understand that Facebook started react-native having mobile in mind, but should it stay like that?
Or should we try to move react-native as a Platform that let's you choose which platform you target and treat all of them equally in terms of difficulty to "Add a platform".
I'm imagining a world where you could start a React Native app with only iOS in it, but then add platforms as you go in an easy way, the way react-native-windows seems to handle it.
In the same way, you could start a React Native app with only windows, and then add MacOS and Linux support iterations after iterations.
So I guess the discussion is : should the integration of all platforms be equal (and not have 2 that are "preferred"). Platforms concerned by this discussion being:
Discussion points
The text was updated successfully, but these errors were encountered: