Files
agent-skills/skills/vercel-react-native-skills/rules/scroll-position-no-state.md
Jason Woltje f5792c40be feat: Complete fleet — 94 skills across 10+ domains
Pulled ALL skills from 15 source repositories:
- anthropics/skills: 16 (docs, design, MCP, testing)
- obra/superpowers: 14 (TDD, debugging, agents, planning)
- coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth)
- better-auth/skills: 5 (auth patterns)
- vercel-labs/agent-skills: 5 (React, design, Vercel)
- antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo)
- Plus 13 individual skills from various repos

Mosaic Stack is not limited to coding — the Orchestrator and
subagents serve coding, business, design, marketing, writing,
logistics, analysis, and more.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 16:27:42 -06:00

83 lines
1.9 KiB
Markdown

---
title: Never Track Scroll Position in useState
impact: HIGH
impactDescription: prevents render thrashing during scroll
tags: scroll, performance, reanimated, useRef
---
## Never Track Scroll Position in useState
Never store scroll position in `useState`. Scroll events fire rapidly—state
updates cause render thrashing and dropped frames. Use a Reanimated shared value
for animations or a ref for non-reactive tracking.
**Incorrect (useState causes jank):**
```tsx
import { useState } from 'react'
import {
ScrollView,
NativeSyntheticEvent,
NativeScrollEvent,
} from 'react-native'
function Feed() {
const [scrollY, setScrollY] = useState(0)
const onScroll = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
setScrollY(e.nativeEvent.contentOffset.y) // re-renders on every frame
}
return <ScrollView onScroll={onScroll} scrollEventThrottle={16} />
}
```
**Correct (Reanimated for animations):**
```tsx
import Animated, {
useSharedValue,
useAnimatedScrollHandler,
} from 'react-native-reanimated'
function Feed() {
const scrollY = useSharedValue(0)
const onScroll = useAnimatedScrollHandler({
onScroll: (e) => {
scrollY.value = e.contentOffset.y // runs on UI thread, no re-render
},
})
return (
<Animated.ScrollView
onScroll={onScroll}
// higher number has better performance, but it fires less often.
// unset this if you need higher precision over performance.
scrollEventThrottle={16}
/>
)
}
```
**Correct (ref for non-reactive tracking):**
```tsx
import { useRef } from 'react'
import {
ScrollView,
NativeSyntheticEvent,
NativeScrollEvent,
} from 'react-native'
function Feed() {
const scrollY = useRef(0)
const onScroll = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
scrollY.current = e.nativeEvent.contentOffset.y // no re-render
}
return <ScrollView onScroll={onScroll} scrollEventThrottle={16} />
}
```