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:
useSharedValue→ JSI ile erişilen, UI runtime’da yaşayan değeruseAnimatedStyle/ worklet’ler → UI thread’de çalışır- 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 FPS | UI FPS | Olası suçlu |
|---|---|---|
| Düşük | İyi | React render, JS logic |
| İyi | Düşük | Worklet overload, layout animasyon, overdraw |
| Düşük | Düşük | Her 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
runOnJSsayı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.