-
-
Notifications
You must be signed in to change notification settings - Fork 1.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
Add fallback to setImmediate for Expo Router static rendering #4665
Conversation
…ndering with Expo Router)
78e40f3
to
1702a27
Compare
Not very sure how to fix failing jest tests. |
Seems like the actual issue is that |
@satya164 I see. If I change the code to the below tests will pass. Do you think the below approach seems more reasonable? If so, I will update both the code and the PR description accordingly. scheduleOnUI<T>(worklet: ShareableRef<T>) {
// In Node.js environments (like when static rendering with Expo Router)
// requestAnimationFrame is unavailable. Do not call side effects in this case.
if (typeof requestAnimationFrame === 'undefined') {
return;
}
// @ts-ignore web implementation has still not been updated after the rewrite, this will be addressed once the web implementation updates are ready
requestAnimationFrame(worklet);
} |
I've tried to understand what's causing the jest problem but still haven't got any solid results. From what I concluded if we use |
Imo it doesn't address the root of the problem - scheduleOnUI being called during SSR/static build |
Sorry to directly comment on this. |
Hi guys. Any workaround to this problem? |
Hey @nDeavor9 and @arivanbastos, can you confirm if this PR resolves the issue? |
Our builds are blocked at the moment. Can we please have a resolution on this? |
@tomekzaw And yes, the error is gone. |
Sorry, haven't had much time to investigate the root cause further, since I'm not very familiar with reanimated's internals. I originally posted the patch on Twitter with minimal changes, so if you're blocked on this you can use pnpm patch or patch-package to workaround this issue as well. Twitter post: |
Here is my patch-package file ✅ expo-router checked |
Hi @javascripter! I took the liberty to commit to your fork, since I got a solution for this that should both work and pass our tests. Turns out at some point in our code (but after creating Please let me know if it works for you - if it does, I think it is safe to merge (after my colleagues review it of course). |
const scheduleOnUI = globalThis.requestAnimationFrame | ||
? isJest() | ||
? mockedRequestAnimationFrame | ||
: globalThis.requestAnimationFrame | ||
: setImmediate; |
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.
- First, can we handle Jest first and then the rest?
const scheduleOnUI = isJest()
? mockedRequestAnimationFrame
: globalThis.requestAnimationFrame || globalThis.setImmediate;
- Can we add a new platform checker
isStaticRendering()
? This way we could write it this way:
const scheduleOnUI = isJest()
? mockedRequestAnimationFrame
: isStaticRendering()
? globalThis.setImmediate
: globalThis.requestAnimationFrame;
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.
-
As far as I understand in this environment
useAnimatedFrame
is unavailable in Jest also and we want to usesetImmediate
there. With this implementation we would always use the mock - maybe it's desired - @javascripter could you give more insight on that? -
This is hard, we have to discuss it tomorrow.
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 used setImmediate in the static rendering environment simply because I tested it in my environment and it seemed to work well for me. As @satya164 said, scheduleOnUI probably doesn't need to execute in that environment so I don't have a concrete reason to prefer setImmediate over the mock in Jest. Always using the mock in Jest seems a reasonable approach to me. @tjzel
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.
- Can we check who exactly calls
scheduleOnUI
here any why? Maybe we can move the call into a conditional. I suspect this might beinitializeUIRuntime
which callsrunOnUIImmediately
.
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.
We actually had a quite fruitful discussion about this and we want to go a step further and improve how Reanimated behaves during Static Renders. This PR can stay as a workaround for the moment, but we will open a proper one in the near future.
@tjzel Hi. Thanks for taking a look at this! Yes, this works for me, and the commit looks good to me as well. |
Looks like there's a conflict — hoping this will get merged soon-ish :) Thanks for all your work here! |
src/reanimated2/initializers.ts
Outdated
global.requestAnimationFrame = (callback: (timestamp: number) => void) => { | ||
return setTimeout(() => callback(performance.now()), 0); | ||
}; | ||
global.requestAnimationFrame = mockedRequestAnimationFrame; |
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.
Since we check globalThis.requestAnimationFrame
, shouldn't we also assign to globalThis
instead of global
?
global.requestAnimationFrame = mockedRequestAnimationFrame; | |
globalThis.requestAnimationFrame = mockedRequestAnimationFrame; |
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.
In our use cases it should be irrelevant what we assign it to according to some docs but I guess it's better practice to use globalThis
.
const scheduleOnUI = globalThis.requestAnimationFrame | ||
? isJest() | ||
? mockedRequestAnimationFrame | ||
: globalThis.requestAnimationFrame | ||
: setImmediate; |
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.
- Can we check who exactly calls
scheduleOnUI
here any why? Maybe we can move the call into a conditional. I suspect this might beinitializeUIRuntime
which callsrunOnUIImmediately
.
@@ -28,7 +38,7 @@ export default class JSReanimated { | |||
|
|||
scheduleOnUI<T>(worklet: ShareableRef<T>) { | |||
// @ts-ignore web implementation has still not been updated after the rewrite, this will be addressed once the web implementation updates are ready | |||
requestAnimationFrame(worklet); | |||
_scheduleOnUI(worklet); |
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.
In our codebase the leading underscore means that the function is a JSI binding. Can we rename it to something else?
https://blog.expo.dev/expo-sdk-49-c6d398cdf740 Having issue in web - expo/expo#23412 - software-mansion/react-native-reanimated#4665 ## Issue Upgrading expo metro as suggested in expo/router#864 (comment) causes problem when running app in `iOS` (not yet tested in android). It freezes in `SplashScreen`. So we need to becareful in merging this `PR`. Currently, it is only working in `web` and freezes in `iOS`.
Summary
In Node.js environments such as when static rendering with Expo Router, requestAnimationFrame is not defined. This results in an error when using libraries that depend on react-native-reanimated like Drawer or when using react-native-reanimated directly in Expo Router with static rendering, causing a hydration mismatch due to rendering errors.
In this PR, I added a fallback to setImmediate if requestAnimationFrame is not defined in
globalThis
. I did not simply replace requestAnimationFrame with setImmediate becausesetImmediate
is slower than requestAnimationFrame in CSR environments. This issue is not applicable to static rendering environments however, so setImmediate is fine there.Test plan