Reanimated

Reanimated, Worklets ve JSI

ReanimatedWorkletsAnimation

UI thread animasyonlarının JSI temeli, worklet kuralları, shared value ve Fabric uyumu. Gesture + layout animasyon için pratik rehber.

EG

Emre Gürbüz

3 Ağustos 2026 · 4 dk okuma

React Native’de akıcı animasyonun düşmanı uzun süre bridge (JS ile native arasındaki eski köprü) oldu. Her frame JS’e gidip JSON ile native’e dönmek 16ms bütçeyi yerdi. 60 FPS, saniyede 60 gidiş-geliş demekti — sürdürülemezdi.

Reanimated (2.x / 3.x), animasyonu UI runtime’daki worklet’lere (UI thread’de çalışan küçük JS fonksiyonları) taşıyarak bunu kırdı. Alttaki motor: JSI (JavaScript Interface — doğrudan native çağrı).

Temel fikir: animasyonu JS framework’ünden çıkarmak

Klasik Animated API her frame’de bridge üzerinden değer gönderirdi. Reanimated modeli:

  1. useSharedValue → JSI ile erişilen, UI runtime’da yaşayan değer
  2. useAnimatedStyle / worklet’ler → UI thread’de çalışır
  3. Gesture Handler ile touch → shared value → style yolu bridge’siz akar

Basit bir spring animasyonu örneği:

import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withSpring,
} from 'react-native-reanimated';

export function SpringBox() {
  const x = useSharedValue(0);
  const style = useAnimatedStyle(() => ({
    transform: [{ translateX: x.value }],
  }));

  return (
    <Animated.View
      style={[{ width: 80, height: 80 }, style]}
      onTouchEnd={() => {
        x.value = withSpring(x.value === 0 ? 120 : 0);
      }}
    />
  );
}

'worklet' directive’i (Bazen Babel plugin gizler) fonksiyonun hangi runtime’a kopyalanacağını belirler. Yanlış runtime’da shared.value okumak klasik hatadır — undefined veya crash.

JSI neden şart?

Shared value düz JS objesi değildir. Native tarafta slot tutulur; JSI host object üzerinden sync erişilir. Bu yüzden:

  • Sync okuma/yazma bridge serileştirmesinden ucuzdur — ama UI thread’i kirletmeyin
  • Hermes + New Arch’da init sırası kritiktir
  • MMKV ile aynı aile: “JS’ten sync native erişim”

Worklet runtime ayrı bir JS context’tir — ana React thread’inden bağımsız. Closure’a sıradan JS fonksiyonu veya React ref kaçırmak release’de kırılır.

Shared value vs React state

Shared value: UI thread’de okunacak animasyon/gesture state’i — React render’a girmez. React state: ekranda kalıcı metin/veri. Kural: frame başına değişen şey shared value; kalıcı veri React state.

useDerivedValue pahalı hesabı UI thread’de cache’ler — ama her frame çalışıyorsa yine bütçe aşılır.

Worklet kuralları

Closure hijyeni: Worklet içinden React state, ref, context erişmeyin. Gerekirse runOnJS ile JS thread’e delege edin — her gesture’da setState spam’i kazancı yer.

Ağır iş yasağı: Büyük JSON.parse, regex, karmaşık dizi işi worklet’te UI’yi öldürür. Veriyi JS’te hazırlayıp shared value’ya yazın.

console.log: Worklet içinde pahalı olabilir; bilinçli kullanın.

runOnJS / runOnUI: UI → JS geçişi maliyeti vardır; saniyede 60 kez yapmayın.

Native module: Worklet’ten doğrudan çağıramazsınız — Reanimated API’sini kullanın.

Fabric ve New Architecture uyumu

Reanimated’ın modern major’ları Fabric’i (yeni renderer) hedefler. Eski sürüm + newArchEnabled=true = garip gesture/layout bug’ları.

Uyum matrisini dokümante edin:

  • Reanimated major ↔ RN sürüm ↔ Gesture Handler sürüm
  • Babel plugin sırası: Reanimated en sonda
  • Production R8/ProGuard keep kuralları (worklet bytecode silinmemeli)

Sürüm uyumsuzluğu “debug’da 60fps, release’de drop” üretebilir.

Layout animations

entering / exiting / Layout liste insert/delete’te güçlüdür. Ama:

  • Büyük listelerde her satıra entering = UI overload
  • FlashList ile kombinasyonda ölçün — virtualization çakışabilir
  • “Her satıra entering” varsayılan olmasın

Gesture-driven animasyon ile layout animasyonu aynı ekranda üst üste bindirmeden profile alın.

Gesture Handler

Reanimated + Gesture Handler birlikte tasarlanmıştır. Gesture.Pan() → shared value → useAnimatedStyle yolu bridge’siz akar.

Yaygın hata: eski Animated.event zihniyeti. Modern yol: GestureDetector + worklet callback.

Pinch + pan gibi simultaneous gesture’larda tek shared value ve doğru config kullanın.

Profiling: hangi thread suçlu?

JS FPSUI FPSOlası suçlu
DüşükİyiReact render, JS logic
İyiDüşükWorklet overload, layout animasyon, overdraw
DüşükDüşükHer iki katman — önce JS, sonra UI

Systrace / Instruments’ta UI thread + Reanimated slice’larına bakın. Tahminle cancelAnimation eklemeyin.

Sık hatalar

  • Shared value’yu React state gibi kullanmak → her frame runOnJS(setState)
  • Worklet’ten API çağrısı
  • Babel plugin sırası yanlış → release-only bug
  • Debug 60fps, release drop → R8 strip veya logger farkı

Production checklist

  • Reanimated Babel plugin sonda
  • Fabric uyumlu Reanimated major
  • Gesture Handler versiyonu uyumlu
  • Hot path’te runOnJS sayısı audit edildi
  • R8/ProGuard keep kuralları güncel
  • Low-end cihazda scroll + gesture test edildi
  • New Arch açık/kapalı smoke test

Özet

Reanimated animasyonu JS framework probleminden çıkarıp UI runtime mühendisliğine çevirir. JSI zemini, worklet kuralları ve Fabric uyumu olmadan “spring ekledik” yetmez. Hangi kodun hangi thread’de çalıştığını bilmek ve 16ms frame bütçesini ona göre harcamak senior barajıdır.


Diğer yazılar