feat: Pass View callbacks to native directly on react-native 0.81+ - #1512
feat: Pass View callbacks to native directly on react-native 0.81+#1512mrousavy wants to merge 1 commit into
Conversation
Since react-native 0.81 (react/react-native#48777, react/react#32119), a View Config can opt out of react-native converting function props to `true` by declaring a `process` function for the prop - which Nitro already does for every prop in `wrapValidAttributes(...)`. That means functions now arrive on the native side as raw `jsi::Function`s, so the `{ f: function }` object workaround (`callback(...)`) is no longer needed: ```diff -<Camera onCaptured={callback((i) => console.log(i))} /> +<Camera onCaptured={(i) => console.log(i)} /> ``` Both paths stay a fast path - the react-native version is resolved once, not per prop: - C++ uses a compile-time `#if` on `REACT_NATIVE_VERSION_*` (`NITRO_RAW_FUNCTION_PROPS`). On 0.81+ the function-prop branch compiles away entirely, on 0.78 - 0.80 the `{ f: … }` object is unwrapped as before. - JS resolves `Platform.constants.reactNativeVersion` once and memoizes it. `callback(...)` is now deprecated and a no-op on react-native 0.81+, and View props accept both a raw function and a wrapped `{ f: … }` object, so existing code keeps working on every supported react-native version. Also fixes removing a function prop on react-native 0.78 - 0.80: react-native sends an explicit `null` for removed props, which used to throw while unwrapping `f`.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6df8ca543b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| #if NITRO_RAW_FUNCTION_PROPS | ||
| // Every prop - including functions - arrives as the raw JSI value it was | ||
| // in JS, so there is nothing to unwrap here. | ||
| return CachedProp<T>::fromJSIValue(*runtime, std::move(value), previousProp); |
There was a problem hiding this comment.
Unwrap legacy callback objects on RN 0.81+
On React Native 0.81+, this unconditional raw-value path sends a legacy { f: function } wrapper into JSIConverter<std::function<...>>, whose fromJSI tries to cast the object itself to a function and throws. This conflicts with the newly exported AllowWrappedFunctions type, which explicitly accepts the wrapper, and breaks older JS bundles or callers constructing the public wrapper format after the native app is upgraded. The added compatibility test does not exercise this case on RN 0.81+ because the updated callback(...) helper returns the raw function there; this path should preserve raw functions while still detecting and unwrapping { f: ... } objects.
Useful? React with 👍 / 👎.
Function props no longer need the
{ f: … }object workaround on react-native 0.81 and above.Why this works now
react-native converts every function prop to
truebefore it reaches native - unless the View Config declares aprocessfunction for that prop. That escape hatch landed in facebook/react#32119 / facebook/react-native#48777 and first shipped in react-native 0.81.0 (verified against the published packages:attributeConfigHasProcessis absent in 0.80.3, present in 0.81.0-rc.0):Nitro already sets
process: (i) => ion every attribute inwrapValidAttributes(...)(added in #853 for the customdiff), so on 0.81+ functions already arrive as rawjsi::Functions - the wrapper was just dead weight.What changed
Both react-native versions keep a fast path - the version is resolved once, never per prop:
#ifonREACT_NATIVE_VERSION_*(NITRO_RAW_FUNCTION_PROPSinRawPropsCompat.hpp). On 0.81+ the function-prop branch inCachedProp::fromRawValue(...)compiles away entirely and functions go straight intoJSIConverter; on 0.78 - 0.80 the{ f: … }object is unwrapped exactly as before.callback(...)readsPlatform.constants.reactNativeVersiononce, memoizes it, and returns the function as-is on 0.81+.callback(...)is now@deprecatedbut still works everywhere, and View props accept both a raw function and a wrapped{ f: … }object, so this is not a breaking change for apps on react-native 0.78 - 0.80 or for existingcallback(...)call sites.Also fixes a latent crash on react-native 0.78 - 0.80: react-native puts an explicit
nullin the props payload for a removed prop, which used to throw invalue.asObject(...)while unwrappingf(e.g. whenhybridRefgoes from set toundefined).Tests
callback(...)" - back-compat coverage for the wrapper.nullcase above.CachedProp.hppagainst react-native 0.85 headers withNITRO_RAW_FUNCTION_PROPSboth1and0.Docs
"Callbacks have to be wrapped" is now "Callbacks", documenting raw functions as the default and
callback(...)as the react-native 0.78 - 0.80 workaround.