【Reactnative】Reanimated 2 での UI スレッドの処理と `worklet` の使い方、 runOnJSについて解説 ReanimatedError: [Reanimated] Tried to synchronously call a non-worklet function on the UI thread
この記事の目次
Reanimated 2 での UI スレッドの処理と worklet
の使い方
React Native のアニメーションやジェスチャー処理を高速で行うために、Reanimated 2
は UI スレッド と JavaScript スレッド をうまく使い分ける機能を提供しています。この中でも重要な機能が worklet
と runOnJS
です。本記事では、これらの使い方や注意点について解説します。
worklet
とは?
worklet
は、React Native Reanimated 2 の UI スレッドで実行する関数 を定義するためのマークです。UI スレッド上で動作するコードはパフォーマンスが向上し、スムーズなアニメーションやジェスチャー処理を実現できます。
worklet
が必要な理由
Reanimated 2 では、アニメーションやジェスチャーをUIスレッドで処理することで、パフォーマンスを大幅に向上させることができます。そのため、UIスレッドで実行したい関数には worklet
を明記する必要があります。
const gesture = Gesture.Pan().onEnd(event => { 'worklet'; // UIスレッドで動作させるために必須 if (event.translationX > 100) { runOnJS(navigation.openDrawer)(); // JavaScriptスレッドでの処理に切り替え } });
上記の例では、ジェスチャーの onEnd
イベントを処理する関数内に 'worklet'
を付けることで、UI スレッドでその関数が実行されることを指示しています。
runOnJS
とは?
runOnJS
は、UI スレッドで動作しているコードから JavaScript スレッドの関数を呼び出すためのメソッド です。UI スレッドで動作しているコードは高速でなければならないため、React Native の UI スレッドで完結する処理を行い、必要に応じて JavaScript スレッドに処理を移すことが求められます。
const gesture = Gesture.Pan().onEnd(event => { 'worklet'; // UIスレッドで動作させるために必須 if (event.translationX > 100) { runOnJS(navigation.openDrawer)(); // JavaScriptスレッドでの処理に切り替え } });
このコードでは、ジェスチャーイベントが UI スレッドで処理される際に、スワイプ方向が右であれば runOnJS
を使って JavaScript スレッドの navigation.openDrawer
を実行しています。
worklet
と runOnJS
を使うタイミング
worklet
が必要な場合
- ジェスチャー処理: ジェスチャーの位置や速度を元にアニメーションを実行する場合、UI スレッドで処理するために
worklet
が必要です。 - アニメーションの管理: アニメーションの更新処理も UI スレッドで行う必要があり、これにも
worklet
を付けることが求められます。
runOnJS
が必要な場合
- JavaScript スレッドでの処理: UI スレッドから JavaScript スレッドの関数を呼び出すために
runOnJS
を使います。例えば、navigation.openDrawer()
のように画面遷移を行う場合です。
よくあるエラーとその回避方法
エラー: ReanimatedError: [Reanimated] Tried to synchronously call a non-worklet function on the UI thread
このエラーは、UI スレッドから直接 JavaScript スレッドの関数を呼び出そうとした場合に発生します。解決策は、runOnJS
を使って JavaScript スレッドで関数を実行することです。
// エラー発生例 const gesture = Gesture.Pan().onEnd(event => { if (event.translationX > 100) { navigation.openDrawer(); // ここでエラーが発生 } });
上記のコードでは、UI スレッド上で navigation.openDrawer()
を呼び出そうとしているため、エラーが発生します。これを解決するために、runOnJS
を使って JavaScript スレッドに移行します。
// 解決方法 const gesture = Gesture.Pan().onEnd(event => { 'worklet'; // UIスレッドで動作させるために必須 if (event.translationX > 100) { runOnJS(navigation.openDrawer)(); // JavaScriptスレッドでの処理に切り替え } });
worklet
を使う際の注意点
- UI スレッドで実行される:
worklet
内のコードは UI スレッドで実行されるため、パフォーマンス向上に繋がりますが、非同期処理や重い計算を避けるようにしましょう。 - アニメーションやジェスチャーに特化:
worklet
は主にアニメーションやジェスチャー処理に使用され、通常の JavaScript ロジックには使用しません。
まとめ
worklet
は UI スレッドで動作する関数を定義するために使用します。runOnJS
は UI スレッドから JavaScript スレッドの関数を呼び出すための方法です。- ジェスチャーやアニメーション処理を行う際には、
worklet
とrunOnJS
を適切に使い分けることが重要です。
Reanimated 2 を活用することで、React Native アプリのパフォーマンスを大幅に向上させることができますが、適切な使い方を理解することが必要です。