-
Notifications
You must be signed in to change notification settings - Fork 0
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
Discussion #1
Comments
Next steps
For one thing, I'm not clear what's useful to us in ReactCommon. For another thing, we're still very far away from doing anything useful with views, so we should continue to focus on solving that part. See the interface for RCTView: declare class RCTView extends UIView {
static alloc(): RCTView; // inherited from NSObject
static appearance(): RCTView; // inherited from UIAppearance
static appearanceForTraitCollection(trait: UITraitCollection): RCTView; // inherited from UIAppearance
static appearanceForTraitCollectionWhenContainedIn(trait: UITraitCollection, ContainerClass: typeof NSObject): RCTView; // inherited from UIAppearance
static appearanceForTraitCollectionWhenContainedInInstancesOfClasses(trait: UITraitCollection, containerTypes: NSArray<typeof NSObject> | typeof NSObject[]): RCTView; // inherited from UIAppearance
static appearanceWhenContainedIn(ContainerClass: typeof NSObject): RCTView; // inherited from UIAppearance
static appearanceWhenContainedInInstancesOfClasses(containerTypes: NSArray<typeof NSObject> | typeof NSObject[]): RCTView; // inherited from UIAppearance
static autoAdjustInsetsForViewWithScrollViewUpdateOffset(parentView: UIView, scrollView: UIScrollView, updateOffset: boolean): void;
static contentInsetsForView(curView: UIView): UIEdgeInsets;
static new(): RCTView; // inherited from NSObject
borderBottomColor: any;
borderBottomEndRadius: number;
borderBottomLeftRadius: number;
borderBottomRightRadius: number;
borderBottomStartRadius: number;
borderBottomWidth: number;
borderColor: any;
borderEndColor: any;
borderEndWidth: number;
borderLeftColor: any;
borderLeftWidth: number;
borderRadius: number;
borderRightColor: any;
borderRightWidth: number;
borderStartColor: any;
borderStartWidth: number;
borderStyle: RCTBorderStyle;
borderTopColor: any;
borderTopEndRadius: number;
borderTopLeftRadius: number;
borderTopRightRadius: number;
borderTopStartRadius: number;
borderTopWidth: number;
borderWidth: number;
hitTestEdgeInsets: UIEdgeInsets;
onAccessibilityAction: (p1: NSDictionary<any, any>) => void;
onAccessibilityEscape: (p1: NSDictionary<any, any>) => void;
onAccessibilityTap: (p1: NSDictionary<any, any>) => void;
onMagicTap: (p1: NSDictionary<any, any>) => void;
pointerEvents: RCTPointerEvents;
removeClippedSubviews: boolean;
updateClippedSubviews(): void;
} Note how declare class RCTShadowView extends NSObject implements RCTComponent {
static alloc(): RCTShadowView; // inherited from NSObject
static new(): RCTShadowView; // inherited from NSObject
static yogaConfig(): interop.Pointer | interop.Reference<any>;
alignContent: YGAlign;
alignItems: YGAlign;
alignSelf: YGAlign;
aspectRatio: number;
readonly availableSize: CGSize;
readonly borderAsInsets: UIEdgeInsets;
borderBottomWidth: number;
borderEndWidth: number;
borderLeftWidth: number;
borderRightWidth: number;
borderStartWidth: number;
borderTopWidth: number;
borderWidth: number;
bottom: YGValue;
readonly compoundInsets: UIEdgeInsets;
readonly contentFrame: CGRect;
direction: YGDirection;
display: YGDisplay;
end: YGValue;
flex: number;
flexBasis: YGValue;
flexDirection: YGFlexDirection;
flexGrow: number;
flexShrink: number;
flexWrap: YGWrap;
height: YGValue;
intrinsicContentSize: CGSize;
justifyContent: YGJustify;
layoutMetrics: RCTLayoutMetrics;
left: YGValue;
margin: YGValue;
marginBottom: YGValue;
marginEnd: YGValue;
marginHorizontal: YGValue;
marginLeft: YGValue;
marginRight: YGValue;
marginStart: YGValue;
marginTop: YGValue;
marginVertical: YGValue;
maxHeight: YGValue;
maxWidth: YGValue;
minHeight: YGValue;
minWidth: YGValue;
newView: boolean;
onLayout: (p1: NSDictionary<any, any>) => void;
overflow: YGOverflow;
padding: YGValue;
readonly paddingAsInsets: UIEdgeInsets;
paddingBottom: YGValue;
paddingEnd: YGValue;
paddingHorizontal: YGValue;
paddingLeft: YGValue;
paddingRight: YGValue;
paddingStart: YGValue;
paddingTop: YGValue;
paddingVertical: YGValue;
position: YGPositionType;
right: YGValue;
rootView: RCTRootShadowView;
size: CGSize;
start: YGValue;
readonly superview: RCTShadowView;
top: YGValue;
viewName: string;
width: YGValue;
readonly yogaNode: interop.Pointer | interop.Reference<any>;
readonly debugDescription: string; // inherited from NSObjectProtocol
readonly description: string; // inherited from NSObjectProtocol
readonly hash: number; // inherited from NSObjectProtocol
readonly isProxy: boolean; // inherited from NSObjectProtocol
reactTag: number; // inherited from RCTComponent
rootTag: number; // inherited from RCTComponent
readonly superclass: typeof NSObject; // inherited from NSObjectProtocol
readonly // inherited from NSObjectProtocol
canHaveSubviews(): boolean;
class(): typeof NSObject;
clearLayout(): void;
conformsToProtocol(aProtocol: any /* Protocol */): boolean;
didSetProps(changedProps: NSArray<string> | string[]): void;
didUpdateReactSubviews(): void;
dirtyLayout(): void;
insertReactSubviewAtIndex(subview: RCTShadowView, atIndex: number): void;
isEqual(object: any): boolean;
isKindOfClass(aClass: typeof NSObject): boolean;
isMemberOfClass(aClass: typeof NSObject): boolean;
isReactRootView(): boolean;
isYogaLeafNode(): boolean;
layoutSubviewsWithContext(layoutContext: RCTLayoutContext): void;
layoutWithMetricsLayoutContext(layoutMetrics: RCTLayoutMetrics, layoutContext: RCTLayoutContext): void;
layoutWithMinimumSizeMaximumSizeLayoutDirectionLayoutContext(minimumSize: CGSize, maximumSize: CGSize, layoutDirection: UIUserInterfaceLayoutDirection, layoutContext: RCTLayoutContext): void;
measureLayoutRelativeToAncestor(ancestor: RCTShadowView): CGRect;
performSelector(aSelector: string): any;
performSelectorWithObject(aSelector: string, object: any): any;
performSelectorWithObjectWithObject(aSelector: string, object1: any, object2: any): any;
reactSubviews(): NSArray<RCTComponent>;
reactSuperview(): RCTShadowView;
reactTagAtPoint(point: CGPoint): number;
removeReactSubview(subview: RCTShadowView): void;
respondsToSelector(aSelector: string): boolean;
retainCount(): number;
self(): this;
setLocalData(localData: NSObject): void;
sizeThatFitsMinimumSizeMaximumSize(minimumSize: CGSize, maximumSize: CGSize): CGSize;
viewIsDescendantOf(ancestor: RCTShadowView): boolean;
} React Native UI architectureIn order to implement any of the React Native views as NativeScript View elements (more specifically, as extensions of LayoutBase or FlexboxLayout), we need to understand how React Native updates its UI. i.e. what is the purpose of RCTView, RCTViewManager, RCTUIManager, and RCTShadowView? Which of these are responsible for what? Do we need the bridge or not? BackgroundUnderstanding React Native Architecture: Create native UI component in React Native React.js Conf 2016 - Tadeu Zagallo - Optimising React Native: Tools and Tips Conclusion so farReact Native maintains a shadow UI hierarchy. i.e. For each node you add to your real UI hierarchy, it adds one to a "shadow UI hierarchy" as well, which is responsible for calculating layout. During each render, once the layout is determined in the shadow UI, React Native applies those layout changes to our real UI. These comments in RCTShadowView.h are particularly informative:
NativeScript UI architectureIn order to plug these bits of React Native functionality into NativeScript, we also need to understanding how the NativeScript UI works. When does it update? How does it layout? How (and when) do the wrapper components coordinate their native views? Those sorts of things. BackgroundConclusion so farNativeScript does a measure pass based on certain properties like width and height, followed by a layout pass based on certain properties like margin. Work so farI'm trying to create a NativeScript element called RNLayout, which is an extension of FlexboxLayout. It will own an RCTShadowView and an RCTView. Upon receiving an event from NativeScript to add/remove a child node, it will add that child node's shadow view to its own shadow view. And upon receiving layout change events from NativeScript, it will inform its own shadow view to recalculate its layout (and the layout of its children, recursively). On the measure pass, it will apply those changes to the RCTView. Well, that's the plan, but it's seriously complex and I'm not confident I'll be able to finish it, due to lack of understanding of both React Native and NativeScript, and lack of contacts to talk to who know how either framework's systems work. But that's where I am, anyway.
I'm not thinking that far yet. I'm not familiar enough with its role in React Native to know what we might do with it. It may or may not be redundant. |
Some more end-to-end notes on the React Native rendering process, determined by reverse-engineering as far as I can tell: Original, in Chinese: https://www.jianshu.com/p/5cc61ec04b39 English translation: https://www.programmersought.com/article/93734568742/ And lots of internals documented here, particularly on the rendering process – but again, only in Chinese. Google translation into English here. |
I just think that:
After that we can decide depending on what we want..
Or?
The former is much more desirable to me. (I can put react native project into ns and get language bindings and keep on working with rn components and start right away, if I want - I can also start exploring ns components in some places) |
I've asked Evan Bacon if he could share his Webpack config for running React Native iOS/Android via Webpack. If he can, then we'd have a basis for consuming the JS code of We wouldn't be able to (easily) mix NativeScript and React Native views, and we'd only be able to support React projects (not Vue/Angular/Svelte), but with that approach, we would be able to code completely as if it were a React Native app, but with the added benefit of NativeScript's ability to call native APIs via JS. Extra work would be needed to support things like autolinking of React Native native modules, but it would still be a huge step forwards. |
There is a mention of a bridgeless mode |
I saw that – it's undocumented, so I wouldn't know how to use it (nor whether it's actually a completed feature or just experimental). Looks exciting, but will have to take things one step at a time. |
It looks incomplete to me. Also it seems |
I found the project that lets you build React Native apps using Webpack (rather than Metro). It looks pretty complicated, so it won't be easy, but it does provide the foundation for adapting our NativeScript Webpack config so that a NativeScript project could directly consume React Native code: |
Most components that end in native implementation seems to use There is
And also: |
So far, I think it seems every ui instance is being
...Looking around in NativeComponentRegistry it seems to be the case for bridge and bridgeless. |
I'ts Here is some information I found. |
I've got a React Native app building using only Webpack (rather than Metro): No NativeScript involved yet; it's a pure React Native project. But following this proof-of-concept, I can study its webpack config and see if there's a way to merge that into a NativeScript project's Webpack config, allowing a NativeScript project to consume React Native JS code. |
There is one initial get call when the importing of components are made and i'ts possible to call it directly in a rn project directly like so:
There is a mentioning of 3d party actors that a may wanting to call it:
What you get is all start/default values and props, the "config". I think these ultimately come from cpp over the bridge. I'ts also related to There is
None of this is called again when trigger a rerender (changing color of View). |
It probably occurs sometime during "App starts" event (looking at the diagram). I think the ui templates are retrieved (the above). I don't think this initial call is one of the call that "Return dimensions" I still have to find js call over bridge, which should occur every render.. and log properties. (But is this a get call made from the bridge maybe?) |
.. Trying to find the instantiation by looking at the rendering. There are
The following blog mentions "Fabric" as being part of the rearchitecting: https://formidable.com/blog/2019/fabric-turbomodules-part-3/. It does not seem to be in production yet Concerning the "Turbo Modules", |
It seems the rendering makes calls to There is a chance it can work together on it's own though once it's possible to call rn in ns as we said on slack |
I think we should focus on calling:
..Or just something that might not be using bridge. Just to get that working first. |
All JS APIs involving native functionality use the bridge. I can’t think of many that don’t involve native functionality. Maybe some StyleSheet APIs? |
My own idea involve trying to get the components out of rn, in some nice way by maybe replacing/overwriting modules - like the UIManager. But I don't understand why you expect native parts to work when the non native parts don't work. (I assume the I stopped investigating the UIMangar amongst other because I wanted to make sure I could call something (and then continue on by experimenting) |
I believe I was able to import and log React Native’s I honestly might be misremembering, however. Was focusing more on getting RCTBridge to work, because at that point I’d expect everything to work (because we’re bundling code in mostly the same way that Haul does for pure React Native projects). |
So know that
RCTView
etc can be instantiated in nativescript. I think the a next step could be to make a adapted rn version with alteredReactCommon
orLibraries/Components
?All rn components seem to use
codegenNativeComponent
,codegen
- there might be some way to have it it use the nativescript bindings there instead?The text was updated successfully, but these errors were encountered: