Skip to content

Commit

Permalink
docs: Add Worklets page (#6257)
Browse files Browse the repository at this point in the history
This PR adds a Worklets explaination page which is a _highly simplified_
but _extended_ version of
[Fundamentals/Worklets](https://docs.swmansion.com/react-native-reanimated/docs/2.x/fundamentals/worklets)
page from Reanimated v2 documentation.

Requested by @piaskowyk

---------

Co-authored-by: Tomek Zawadzki <[email protected]>
Co-authored-by: Mikołaj Szydłowski <[email protected]>
Co-authored-by: Tomasz Żelawski <[email protected]>
  • Loading branch information
4 people committed Jul 15, 2024
1 parent 2d0ed0b commit 49f6e37
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 16 deletions.
160 changes: 160 additions & 0 deletions packages/docs-reanimated/docs/guides/worklets.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
---
id: worklets
sidebar_label: Worklets
sidebar_position: 1
---

# Worklets

Worklets are short-running JavaScript functions that can run on the [UI thread](/docs/fundamentals/glossary#ui-thread). Reanimated uses worklets to calculate view styles and react to events on the UI thread.

You can create your own worklets using the `'worklet';` directive at the top of a function.

```javascript
function myWorklet() {
'worklet';
console.log('Hello from a worklet');
}
```

The [Reanimated Babel Plugin](https://github.com/software-mansion/react-native-reanimated/blob/main/packages/react-native-reanimated/plugin/README-dev.md#basics) looks for functions marked with the `'worklet'` directive and converts them into serializable objects. We call this process [workletization](/docs/fundamentals/glossary#to-workletize). These objects can then be copied and run over on the UI thread.

Most of the time when working with Reanimated and [Gesture Handler](https://docs.swmansion.com/react-native-gesture-handler/) the code is automatically workletized and run on the UI thread by default.

```javascript
import { useAnimatedStyle } from 'react-native-reanimated';

function App() {
const style = useAnimatedStyle(() => {
// Running on the UI thread
return { opacity: 0.5 };
});
}
```

Functions marked with `'worklet';` aren't [hoisted](https://developer.mozilla.org/en-US/docs/Glossary/Hoisting). Besides affecting hoisting, the `'worklet';` directive has no effect on the [JavaScript thread](/docs/fundamentals/glossary#javascript-thread).

You can use [`runOnUI`](/docs/threading/runOnUI) to manually schedule worklet execution on the UI thread:

```javascript
function myWorklet() {
'worklet';
console.log('Hello from the UI thread');
}

function onPress() {
runOnUI(myWorklet)();
}
```

You can pass arguments to worklets.

```javascript
function myWorklet(greeting) {
'worklet';
console.log(`${greeting} from the UI thread`);
}

function onPress() {
runOnUI(myWorklet)('Howdy');
}
```

Worklets are [closures](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures). They can access variables declared outside of their own scope. Only variables referenced in the worklet body will be captured inside the worklet scope.

```javascript
const width = 135.5;

function otherWorklet() {
'worklet';
console.log('Captured width is', width);
}
```

Capturing large JavaScript objects inside of a worklet can lead to performance issues.

```javascript
const theme = {...}; // theme is a large object

function myWorklet() {
'worklet';
console.log(theme.color); // 🚨 referenced `color` but captured the whole `theme` object
}
```

You can get around this problem by first assigning the prop you want to use to a separate variable.

```javascript
const theme = {...};
const color = theme.color;

function myWorklet() {
'worklet';
console.log(color); // ✅ captured only `color`
}
```

Worklets can return data within the same thread.

```javascript
function returningWorklet() {
'worklet';
return "I'm back"; // on the UI thread
}

function someWorklet() {
'worklet';
const what = returningWorklet(); // still on the UI thread
console.log('On the UI thread, other worklet says', what);
}
```

To pass data between UI and JS thread we use [shared values](/docs/fundamentals/glossary#shared-value).

```javascript
import { useSharedValue } from 'react-native-reanimated';

function App() {
const width = useSharedValue(100);

function myWorklet() {
'worklet';
width.value += 50;
}

useEffect(() => {
console.log(width.value); // available on both JS and UI thread
}, []);
}
```

You can run functions on the JS thread from the UI thread with [`runOnJS`](/docs/threading/runOnJS). Most frequently used to call functions that aren't marked with a `'worklet';` directive (i.e. most third-party libraries) or to update the React state.

```javascript
import { router } from 'expo-router';
import { Gesture } from 'react-native-gesture-handler';

function App() {
const tap = Gesture.Tap().onEnd(() => {
// i'm a worklet too!
// highlight-next-line
runOnJS(router.back)();
});
}
```

Functions passed to `runOnJS` must be defined in the [JavaScript thread](/docs/fundamentals/glossary#javascript-thread) scope, i.e. in the component body or the global scope. This code won't work because `myFunction` is defined in the `withTiming` callback, which is only executed in the [UI thread](/docs/fundamentals/glossary#ui-thread):

```javascript
function App() {
const tap = Gesture.Tap().onEnd(() => {
// myFunction is defined on the UI thread 🚨
const myFunction = () => {};
runOnJS(myFunction)(); // 💥
});
}
```

Worklets can run in other runtimes than the one provided by Reanimated. For example [VisionCamera](https://github.com/mrousavy/react-native-vision-camera) and [LiveMarkdown](https://github.com/Expensify/react-native-live-markdown) create their own worklet runtimes.

You can create your own worklet runtimes with [`createWorkletRuntime`](/docs/threading/createWorkletRuntime) function.
2 changes: 1 addition & 1 deletion packages/docs-reanimated/docs/threading/runOnJS.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ import RunOnJSSrc from '!!raw-loader!@site/src/examples/RunOnJS';

```jsx
withTiming(0, {}, () => {
// myFunction is defined on the UI thread
// myFunction is defined on the UI thread 🚨
const myFunction = () => {
// ...
};
Expand Down
32 changes: 17 additions & 15 deletions packages/react-native-reanimated/plugin/README-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,28 @@ If you want to build the plugin but without using explicit `yarn`, do `yarn gene

## Table of contents:

1. [Basics](#basics)
1. [Why do we need this plugin?](#why-do-we-need-this-plugin)
1. [What is a worklet?](#what-is-a-worklet)
1. [How does a worklet work?](#how-does-a-worklet-work)
1. [Why transform worklets?](#why-transform-worklets)
1. [Automatic workletization](#automatic-workletization)
1. [Something doesn't work!](#something-doesnt-work-in-reanimated-plugin)
1. [Plugin's applications](#plugins-applications)
1. [What can be a worklet?](#what-can-be-a-worklet)
1. [Inline styles support](#inline-styles-support)
1. [How to debug?](#how-to-debug-reanimated-babel-plugin)
1. [Implementation](#implementation)
1. [Logic flowchart](#reanimated-babel-plugin-flowchart)
1. [Plugin's dependencies](#dependencies-of-reanimated-babel-plugin)
- [Reanimated Babel plugin](#reanimated-babel-plugin)
- [Table of contents:](#table-of-contents)
- [Basics](#basics)
- [Why do we need this plugin?](#why-do-we-need-this-plugin)
- [What is a worklet?](#what-is-a-worklet)
- [How does a worklet work?](#how-does-a-worklet-work)
- [Why transform worklets?](#why-transform-worklets)
- [Automatic workletization](#automatic-workletization)
- [Something doesn't work in Reanimated Plugin!](#something-doesnt-work-in-reanimated-plugin)
- [Plugin's applications](#plugins-applications)
- [What can be a worklet?](#what-can-be-a-worklet)
- [Inline styles support](#inline-styles-support)
- [How to debug Reanimated Babel plugin?](#how-to-debug-reanimated-babel-plugin)
- [Implementation](#implementation)
- [Reanimated Babel plugin flowchart](#reanimated-babel-plugin-flowchart)
- [Dependencies of Reanimated Babel plugin](#dependencies-of-reanimated-babel-plugin)

## Basics

### Why do we need this plugin?

Reanimated is all about executing the code directly on the UI thread whenever possible to avoid expensive and troublesome communication between those two threads. Since UI and JS (React-Native) contexts are separate, we somehow need to pass the functions (and their arguments) from the JS thread to the UI thread. That's why we need **worklets**. If you haven't yet, we strongly recommend reading [the official documentation on worklets](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/glossary/#worklet) first.
Reanimated is all about executing the code directly on the UI thread whenever possible to avoid expensive and troublesome communication between these two threads. Since UI and JS (React-Native) contexts are separate, we somehow need to pass the functions (and their arguments) from the JS thread to the UI thread. That's why we need **worklets**. If you haven't yet, we strongly recommend reading [the official documentation on worklets](https://docs.swmansion.com/react-native-reanimated/docs/guides/worklets) first.

### What is a worklet?

Expand Down

0 comments on commit 49f6e37

Please sign in to comment.