-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Allow worklet referencing in plugin #5911
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
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
tjzel
commented
Apr 17, 2024
tjzel
commented
Apr 17, 2024
…m/software-mansion/react-native-reanimated into @tjzel/plugin/worklet-referencing
tomekzaw
reviewed
Apr 26, 2024
bartlomiejbloniarz
approved these changes
May 29, 2024
Contributor
bartlomiejbloniarz
left a 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.
🚀
3 tasks
This was referenced Jul 25, 2024
r0h0gg6
pushed a commit
to r0h0gg6/react-native-reanimated
that referenced
this pull request
Jul 28, 2025
## Why
Currently, when handling worklet callbacks, the user has either to mark
a function directly with `worklet` directive or define the worklet as an
inline argument.
```tsx
// This will work:
const styleFactory = () => {
'worklet';
return { backgroundColor: 'blue' };
};
const animatedStyle = useAnimatedStyle(styleFactory);
// This will work as well:
const animatedStyle = useAnimatedStyle(() => ({ backgroundColor: blue }));
// However this won't
const styleFactory = () => {
return { backgroundColor: 'blue' };
};
const animatedStyle = useAnimatedStyle(styleFactory); // error - style factory is not a worklet!
```
This pull request allows the user to define a worklet outside of a hook
argument to get it autoworkletized. Keep in mind that it still has some
boundaries:
1. Worklet has to be defined in the same file. It cannot be imported.
2. Worklet has to be defined before it's used.
3. Worklet cannot be defined via an expression (for example, using an
`?` operator). This however could be adjusted in the future.
## What
We are allowing the following constructions to be autoworkletized.
`useAnimatedStyle` will be used as a example hook here, it will work
with every bit of our API that facilitates autoworkletization.
### Function declarations
```ts
function worklet() {
// Some UI relevant code.
}
const animatedStyle = useAnimatedStyle(worklet);
```
### Function expressions
```ts
const worklet = function() {
// Some UI relevant code.
}
const animatedStyle = useAnimatedStyle(worklet);
```
### Arrow function expression
```ts
const worklet = () => {
// Some UI relevant code.
}
const animatedStyle = useAnimatedStyle(worklet);
```
### Object methods
This is a special case, used for example by `useAnimatedScrollHandler`.
```ts
const handler = {
onScroll: () => {
// Some UI relevant code.
}
}
useAnimatedScrollHandler(handler);
```
It doesn't matter if the method is actually an arrow function or
function expression. Keep in mind that it actually has to be defined in
place. As of now we don't support such deep cases as:
```ts
const onScroll = () => {
// Some UI relevant code.
}
const handler = {
onScroll
}
useAnimatedScrollHandler(handler);
```
## How
Changes implemented here are quite simple. The only thing we do is
search the scope of a given context (that's usually a function) for a
referenced identifier. For example:
```ts
const animatedStyle = useAnimatedStyle(styleFactory);
```
The `styleFactory` identifier is referenced in the `useAnimatedStyle`
call. We then search the scope of the function for a reference to
`styleFactory` and look for variable declarations, variable assignments,
and function declarations.
1. Variable declarations:
```ts
let styleFactory = () => {
// Some UI relevant code.
}
```
2. Variable assignments:
```ts
let styleFactory;
// ...
styleFactory = () => {
// Some UI relevant code.
}
```
3. Function declarations:
```ts
function styleFactory() {
// Some UI relevant code.
}
```
If we find one of these, that can be autoworkletized, we do it. If there
are multiple objects that can be autoworkletized, we pick the first one
using the following rules:
1. Function declarations are picked first.
2. Variable assignments are picked second. In case of multiple variable
assignments, we pick the last one.
3. Variable declarations are picked last.
Therefore in the following code:
```ts
let styleFactory = function foo() {
// Some UI relevant code.
}
styleFactory = function bar() {
// Some UI relevant code.
}
styleFactory = function baz() {
// Some UI relevant code.
}
const animatedStyle = useAnimatedStyle(styleFactory);
```
Only the `function baz` will be autoworkletized. Keep in mind that this
is just an edge-case scenario. Please don't ever write such code when
using worklets!
### Scoping
We also support multiple scoping, for example:
```ts
function foo(){
const styleFactory = () => {
// Some UI relevant code.
}
function bar(){
const animatedStyle = useAnimatedStyle(styleFactory);
}
}
```
Will work as expected. It follows the same rules as above. For now we
don't support variable shadowing - expect undefined behavior there.
### Notes
Currently we expect the worklet variable not to be reassigned after it's
been used. For example, the following code will not work:
```ts
const styleFactory = () => {
// Some UI relevant code.
}
const animatedStyle = useAnimatedStyle(styleFactory);
styleFactory = () => {
// Some UI relevant code.
}
```
Because only the last assignment will be workletized. The first
assignment will not be transformed and `useAnimatedStyle` will throw an
error. This is desired behavior, since reassigning worklet variables is
considered an anti-pattern right now.
## Test plan
- [ ] Add tests for all the above cases.
- [ ] Confirm that current errors remain informative in cases where this
mechanism doesn't apply.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Why
Currently, when handling worklet callbacks, the user has either to mark a function directly with
workletdirective or define the worklet as an inline argument.This pull request allows the user to define a worklet outside of a hook argument to get it autoworkletized. Keep in mind that it still has some boundaries:
?operator). This however could be adjusted in the future.What
We are allowing the following constructions to be autoworkletized.
useAnimatedStylewill be used as a example hook here, it will work with every bit of our API that facilitates autoworkletization.Function declarations
Function expressions
Arrow function expression
Object methods
This is a special case, used for example by
useAnimatedScrollHandler.It doesn't matter if the method is actually an arrow function or function expression. Keep in mind that it actually has to be defined in place. As of now we don't support such deep cases as:
How
Changes implemented here are quite simple. The only thing we do is search the scope of a given context (that's usually a function) for a referenced identifier. For example:
The
styleFactoryidentifier is referenced in theuseAnimatedStylecall. We then search the scope of the function for a reference tostyleFactoryand look for variable declarations, variable assignments, and function declarations.If we find one of these, that can be autoworkletized, we do it. If there are multiple objects that can be autoworkletized, we pick the first one using the following rules:
Therefore in the following code:
Only the
function bazwill be autoworkletized. Keep in mind that this is just an edge-case scenario. Please don't ever write such code when using worklets!Scoping
We also support multiple scoping, for example:
Will work as expected. It follows the same rules as above. For now we don't support variable shadowing - expect undefined behavior there.
Notes
Currently we expect the worklet variable not to be reassigned after it's been used. For example, the following code will not work:
Because only the last assignment will be workletized. The first assignment will not be transformed and
useAnimatedStylewill throw an error. This is desired behavior, since reassigning worklet variables is considered an anti-pattern right now.Test plan