Usage
April 17, 2024 ยท View on GitHub
react-native-worklets-core can be used either as a standalone library to run Worklets on a background context, or as an integration with another third-party library to run code on a custom context.
Standalone
To run any JS Function on a background Thread, convert it to a worklet. As an example, this is how you can compute the fibonacci sequence in JS, without blocking the main JS Thread:
const fibonacci = (num: number): number => {
'worklet'
if (num <= 1) return num;
let prev = 0, curr = 1;
for (let i = 2; i <= num; i++) {
let next = prev + curr;
prev = curr;
curr = next;
}
return curr;
}
const context = Worklets.defaultContext
const result = await context.runAsync(() => {
'worklet'
return fibonacci(50)
})
console.log(`Fibonacci of 50 is ${result}`)
Use runOnJS to call back to JS:
const [age, setAge] = useState(30)
function something() {
'worklet'
Worklets.runOnJS(() => setAge(50))
}
Memoize
Both the runAsync and runOnJS functions are convenience methods for createRunAsync and createRunOnJS. For frequent calls, prefer the prepare... equivalent instead to memoize the caller function.
Hooks
Worklets also provides three utility hooks:
useWorklet
Uses a memoized Worklet that can be called from the JS context, or from within another Worklet context:
function App() {
const worklet = useWorklet('default', () => {
'worklet'
console.log("hello from worklet!")
}, [])
worklet()
}
useRunOnJS
Uses a memoized callback to the JS context that can be called from within a Worklet context:
function App() {
const sayHello = useRunOnJS(() => {
console.log("hello from JS!")
}, [])
const worklet = useWorklet('default', () => {
'worklet'
console.log("hello from worklet!")
sayHello()
}, [sayHello])
worklet()
}
useSharedValue
Uses a SharedValue instance that can be read from- and written to by both a JS context and a Worklet context at the same time:
function App() {
const something = useSharedValue(5)
const worklet = useWorklet('default', () => {
'worklet'
something.value = Math.random()
}, [something])
}
The SharedValue will create a C++ based Proxy implementation for Arrays and Objects, so that any read- or write-operations on the Array/Object are thread-safe.
Separate Contexts
You can also create specific contexts (Threads) to run Worklets on:
const context1 = Worklets.createContext('my-new-thread-1')
context1.runAsync(() => {
'worklet'
console.log("Hello from context #1!")
})
...and even nest them without ever crossing the JavaScript Thread:
const context1 = Worklets.createContext('my-new-thread-1')
const context2 = Worklets.createContext('my-new-thread-2')
context1.runAsync(() => {
'worklet'
console.log("Hello from context #1!")
context2.runAsync(() => {
'worklet'
console.log("Hello from context #2!")
})
})
Integration
To integrate react-native-worklets-core in your library, first install the package:
Dependency Installation
iOS:
- Add
react-native-worklets-coreto your podspec:s.dependency "react-native-worklets-core"
Android:
- Add
react-native-worklets-coreto yourbuild.gradle:implementation project(":react-native-worklets-core") - Add
react-native-worklets-coreto yourCMakeLists.txt:find_package(react-native-worklets-core REQUIRED CONFIG) # ... target_link_libraries( ${PACKAGE_NAME} # ... other libs react-native-worklets-core::rnworklets )
Then, from C++ you can convert Functions to Worklets and call them on any Thread:
Usage
- Include the Headers:
#include <JsiWorkletContext.h> #include <JsiWorklet.h> - On the main JavaScript Thread (
mqt_js), create your Worklet Context:auto jsCallInvoker = ... // <-- facebook::react::CallInvoker auto customThread = ... // <-- your custom Thread auto runOnJS = [jsCallInvoker](std::function<void()>&& f) { // Run on React JS Runtime jsCallInvoker->invokeAsync(std::move(f)); }; auto runOnWorklet = [customThread](std::function<void()>&& f) { // Run on your custom Thread customThread->invokeAsync(std::move(f)); }; _workletContext = std::make_shared<RNWorklet::JsiWorkletContext>("MyLibrary"); _workletContext->initialize("MyLibrary", jsRuntime, runOnJS, runOnWorklet); - When a user passes a
jsi::Functionwith the'worklet'directive, you can convert that to a worklet:auto runtime = ... // <-- source jsi::Runtime (main JS) auto func = ... // <-- JS with 'worklet' auto worklet = std::make_shared<RNWorklet::JsiWorklet>(runtime, func); auto workletInvoker = std::make_shared<RNWorklet::WorkletInvoker>(worklet); - Call it on your custom Thread:
_workletContext->invokeOnWorkletThread([=](RNWorklet::JsiWorkletContext*, jsi::Runtime& rt) { jsi::Value someValue(true); workletInvoker->call(rt, jsi::Value::undefined(), &someValue, 1); });