-
Notifications
You must be signed in to change notification settings - Fork 24.3k
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
feat: Allow accessing jsi::Value
on RawProps
#44966
base: main
Are you sure you want to change the base?
Conversation
Base commit: fe06cbf |
RawPropsParser
jsi::Value
on RawProps
cc @cortinico sorry for pinging but please lmk if this is a good or bad idea, and if there's a better way to get the props as jsi::Values. thanks! |
@philIip is the best reviewer here |
I think your high-level use-case here (allowing complex HostObject props to be passed through) makes sense, but we need to figure out a more scalable way to expose this. RawProps can represent both folly::dynamic as well as jsi::Value, and we manage how this get accessed so we can change the internals from diff-based to full JS representation based. Also keep in mind that while Android parses props here, it will pass only a folly::dynamic representation of it through to native, and you'd need use State or other mechanisms to make the HostObject available. |
Okay I see. I only tested on iOS so far, my bad. What do you suggest then? I need a way to access a view component's props as jsi::Values... |
Something @dmytrorykun came across recently is that RawValue eagerly converts the incoming jsi::Value to a folly::dynamic representation, something we want to move away from. So if we can could get that supported (similar to RawProps supporting both representations), we could allow prop conversion (eg using the existing bridging logic) |
Yea I have a similar implementation to the My implementation (not yet OSS) is pretty similar, but has a few important differences. Especially around functions/callbacks (Function/Promise), and custom types (HostObjects/NativeState). So are you saying that I should use RN's Cxx bridging interface, or is there a way I can use my own? Accessing jsi::Value directly on props. |
To be completely precise, it's not |
And in react-native/packages/react-native/ReactCommon/react/renderer/core/RawProps.cpp Lines 174 to 184 in f903f34
So then the approach of accessing |
No, I'm talking about this code in react-native/packages/react-native/ReactCommon/react/renderer/core/RawPropsParser.cpp Lines 140 to 141 in f903f34
This is where we need to hold on to the jsi::Value instead. |
So just for my understanding; is it correct that on Android, the jsi::Value is already gone after RawProps? (-> folly::dynamic)
@javache do you have any alternative solutions? I mean I could try starting fully from scratch to look into mounting views in an entirely different way, I'm not too familiar with how the react renderer / fabric works but to me this would sound like I need to re-invent A TON of stuff, which is not a good idea. |
It's gone once we complete parsing, ie when you have the Props object.
I'm afraid not right now. We're actively looking at this code for perf reasons, and mapping it more closely to jsi::Value is definitely something we're interested in, but it's a non-trivial change. For now, I think the simplest option is to use a (C++) TurboModule to store these JSI values, and pass a (numeric) reference to the stored values as a prop instead. |
Hmm okay I see, thanks. So just to be on the same page, is this the same idea that you're proposing?
JS: class Camera {
render() {
const { device, frameProcessor } = this.props
const deviceRef = makeValueGlobalAndGetReferenceId(device)
const frameProcessorRef = makeValueGlobalAndGetReferenceId(frameProcessor)
return <NativeCamera device={deviceRef} frameProcessor={frameProcessorRef} />
}
} C++: std::unordered_map<double, jsi::Value> globals;
static double latestRefId = 0;
auto func = [](jsi::Runtime&, const jsi::Value&, jsi::Value* args, size_t) -> jsi::Value {
double referenceId = latestRefId++;
// store given value in global (TODO: this is UNSAFE, because it escapes this function and we dont know if it will be consumed right away)
globals[referenceId] = std::move(args[0]);
return jsi::Value(referenceId);
}
runtime.global().setProperty("makeValueGlobalAndGetReferenceId", jsi::Function::createFromHostFunction(..., func)); My React Native Framework's middleman C++: void updateProps() {
// well, this just converts the numbers (deviceRef, frameProcessorRef) to jsi::Values again.
// but this is also unsafe since we are no longer on the JS thread here afaik.
// also we are not 100% sure if values will be consumed (e.g. concurrent updates canceling a batch?)
} Is that what you had in mind as well? This sounds like a really hacky solution, and my TODO comments are a bit concerning |
Another problem I see with the global number reference <-> jsi::Value map approach is that view component functions aren't really trivial to implement. E.g. for a How would I implement this? I could create a global jsi::Function that performs takePhoto(...) and returns a jsi::HostObject, but how do I get a reference to the Camera View in there? nativeId? findNodeHandle? I know this isn't trivial at all, but at first I was thinking that maybe somewhat reinventing the wheel here and building my own JSI based view components (including prop updaters and view component functions) would be a better approach. For that I'd need access to the jsi::Value in RawProps though, unless there is a lower level at which I can get props? I don't want to re-implement the whole thing starting from React.createElement because then I'd probably break things like Reanimated, as that works through setNativeProps... |
Yes, the approach I described has a number of short-comings. All props parsing from React commits does happen on the JS thread though, so accessing these globals should be safe. We are actively looking at this part of code-base as part of performance improvements, but do not have any immediate plans to make closer integration with jsi::Value part of props parsing. I don't think we want to pull in this PR as-is, as it's quite broad and will limit us in how we can evolve this going forward (same reason the operator() folly::dynamic is deprecated here). |
Summary:
I'm working on a new React Native Framework. It uses pure C++/JSI to create, mount and update a View under Fabric.
Unfortunately the
RawProps
type abstraction is not enough in my case, I need the actualjsi::Value
(which might be ajsi::HostObject
, NativeState, or other custom types I created).Now my idea is to avoid going the
RawPropsParser
route and instead directly access theRawProps
'jsi::Value
(including it'sjsi::Runtime
) to go my route.This will be safe since I immediately convert the
jsi::Value
to other C++ types, and I don't keep that instance (nor thejsi::Runtime
) in memory.My previous approach was extending
RawPropsParser
, but that's a bit more complex since friendship is not inheritable in C++ and I also feel like we don't need the whole prop parsing loop. In my case, all of the parsing is statically generated with templates - there will be no iterations.If there's a simpler or better alternative to this, I'm all ears - but from what I'm seeing in the codebase there is no better direct point to intercept all
jsi::Value
s for view components. Please correct me if I'm wrong (cc @philIip ?)Changelog:
[INTERNAL] [CHANGED] - Allow accessing
RawProps
'jsi::Value
directly for React Native FrameworksTest Plan:
I receive
RawProps
(e.g. inImageProps.cpp
), and directly accessjsi::Value
from there to convert the object to a C++ object (or individual C++ keys).