-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Update how Gesture Handler exposes setGestureState
to the Reanimated UI runtime
#3207
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
base: main
Are you sure you want to change the base?
Conversation
4b6cb29
to
039e228
Compare
|
||
if (REANIMATED_AVAILABLE) { | ||
// When Reanimated is available, setGestureState should be defined | ||
if (global._setGestureStateNew) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use globalThis
instead of global
, I believe @tjzel can provide some more insights on that.
if (global._setGestureStateNew) { | |
if (globalThis._setGestureState) { |
Also, I'm not sure if we need to include "new" in the name, how about using _setGestureState
instead and renaming setGestureState
to setGestureStateOld` as suggested in another comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer not to change the previous method - it's consumed by RNGH, but it's defined entirely on the Reanimated part. Changing it will very likely break the integration between Reanimated and older versions of RNGH.
And _setGestureStateNew
is subject to change, though _setGestureState
will be hard to beat 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using globalThis
will help you with exotic web environments as it should point to the right context of window
, global
, self
, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the code to use globalThis
. I also changed the name - it will be possible to change the state of the gesture from the JS thread as well, so names are:
_setGestureStateSync
on UI runtime_setGestureStateAsync
on JS runtime
src/RNGestureHandlerModule.ts
Outdated
|
||
function tryInstallUIBindings() { | ||
if (global._WORKLET_RUNTIME) { | ||
console.log('Installing Gesture Handler UI bindings'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to keep those console.logs in .ts
file? Maybe let's convert them to comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, but they are useful for development. One of the todos from the PR description is get rid of the console logs
src/RNGestureHandlerModule.ts
Outdated
setTimeout(() => { | ||
console.log('Waiting for worklet runtime to be available'); | ||
tryInstallUIBindings(); | ||
}, 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not good, did this actually happen to you? I believe that _WORKLET_RUNTIME
is injected synchronously once we import react-native-reanimated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reanimated's index.ts
doesn't have any side-effects and just requiring it might not trigger the TurboModule to initialize. I think it would be better to require executeOnUIRuntimeSync
, then invoke it with a HostFunction that will invoke native tryInstallUIBindings
, to make sure the pipeline is all set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I switched to a different approach - instead of decorating the UI runtime immediately, I do it lazily when the first gesture is created. This way, the possible delay should be mitigated entirely.
setGestureState
to the ui runtimesetGestureState
to the Reanimated UI runtime
Additionally, I'd like to rename |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice work overall, I like the removals 😎
friend HybridBase; | ||
jsi::Runtime* rnRuntime = nullptr; | ||
|
||
jni::global_ref<RNGestureHandlerModule::javaobject> javaPart_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You must release javaPart_
when native modules are invalidated, otherwise you'll create a retain cycle and leak memory.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean in the destructor of the C++ class or something else?
src/RNGestureHandlerModule.ts
Outdated
setTimeout(() => { | ||
console.log('Waiting for worklet runtime to be available'); | ||
tryInstallUIBindings(); | ||
}, 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reanimated's index.ts
doesn't have any side-effects and just requiring it might not trigger the TurboModule to initialize. I think it would be better to require executeOnUIRuntimeSync
, then invoke it with a HostFunction that will invoke native tryInstallUIBindings
, to make sure the pipeline is all set.
apple/RNGestureHandlerModule.mm
Outdated
jsi::Runtime &rt = *_rnRuntime; | ||
|
||
const auto arrayBufferValue = | ||
rt.global().getProperty(rt, "_WORKLET_RUNTIME").getObject(rt).getArrayBuffer(rt).data(rt); | ||
const auto uiRuntimeAddress = reinterpret_cast<uintptr_t *>(&arrayBufferValue[0]); | ||
jsi::Runtime &uiRuntime = *reinterpret_cast<jsi::Runtime *>(*uiRuntimeAddress); | ||
|
||
__weak RNGestureHandlerModule *weakSelf = self; | ||
|
||
auto setGestureStateNew = jsi::Function::createFromHostFunction( | ||
uiRuntime, | ||
jsi::PropNameID::forAscii(uiRuntime, "_setGestureStateNew"), | ||
2, | ||
[weakSelf](jsi::Runtime &runtime, const jsi::Value &, const jsi::Value *args, size_t count) -> jsi::Value { | ||
if (count == 2) { | ||
const auto handlerTag = static_cast<int>(args[0].asNumber()); | ||
const auto state = static_cast<int>(args[1].asNumber()); | ||
|
||
// TODO: expose to JS and dispatch to UI thread if called on JS? | ||
RNGestureHandlerModule *strongSelf = weakSelf; | ||
if (strongSelf != nullptr) { | ||
[strongSelf setGestureState:state forHandler:handlerTag]; | ||
} | ||
} | ||
return jsi::Value::undefined(); | ||
}); | ||
|
||
uiRuntime.global().setProperty(uiRuntime, "_setGestureStateNew", std::move(setGestureStateNew)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why don't we move this code to the same place where isViewFlatteningDisabled
is defined?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It needs to be delayed since there's no guarantee UI runtime will be initialized and injected into the JS one by this point.
08271fd
to
12c9f11
Compare
Description
Changes how
setGestureState
is exposed to the UI runtime. Instead of the weird conditionally adding Reanimated as a dependency on Android and the weird cast on iOS it uses_WORKLET_RUNTIME
const injected by Reanimated into the JS runtime. This allows Gesture Handler to decorate the UI runtime without direct dependencies between the libraries.TODO:
setGestureState
Caution
This works only on the New Architecture (and breaks the old one)
Test plan
TODO