diff --git a/packages/docs-reanimated/docs/guides/worklets.mdx b/packages/docs-reanimated/docs/guides/worklets.mdx new file mode 100644 index 00000000000..a1a6ba2393d --- /dev/null +++ b/packages/docs-reanimated/docs/guides/worklets.mdx @@ -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. diff --git a/packages/docs-reanimated/docs/threading/runOnJS.mdx b/packages/docs-reanimated/docs/threading/runOnJS.mdx index 4993dee92b4..b1a58bd47d1 100644 --- a/packages/docs-reanimated/docs/threading/runOnJS.mdx +++ b/packages/docs-reanimated/docs/threading/runOnJS.mdx @@ -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 = () => { // ... }; diff --git a/packages/react-native-reanimated/plugin/README-dev.md b/packages/react-native-reanimated/plugin/README-dev.md index cc10f64ce22..ab4b65e4eee 100644 --- a/packages/react-native-reanimated/plugin/README-dev.md +++ b/packages/react-native-reanimated/plugin/README-dev.md @@ -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?