@vetala/vetala 0.1.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @vetala/vetala might be problematic. Click here for more details.
- package/CONTRIBUTING.md +77 -0
- package/LICENSE +184 -0
- package/README.md +136 -0
- package/THIRD_PARTY_LICENSES.md +17 -0
- package/dist/src/agent.d.ts +30 -0
- package/dist/src/agent.js +216 -0
- package/dist/src/agent.js.map +1 -0
- package/dist/src/approvals.d.ts +18 -0
- package/dist/src/approvals.js +81 -0
- package/dist/src/approvals.js.map +1 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +87 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/config.d.ts +12 -0
- package/dist/src/config.js +183 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/context-memory.d.ts +7 -0
- package/dist/src/context-memory.js +96 -0
- package/dist/src/context-memory.js.map +1 -0
- package/dist/src/ink/command-suggestions.d.ts +7 -0
- package/dist/src/ink/command-suggestions.js +179 -0
- package/dist/src/ink/command-suggestions.js.map +1 -0
- package/dist/src/ink/ink-terminal-ui.d.ts +36 -0
- package/dist/src/ink/ink-terminal-ui.js +79 -0
- package/dist/src/ink/ink-terminal-ui.js.map +1 -0
- package/dist/src/ink/repl-app.d.ts +9 -0
- package/dist/src/ink/repl-app.js +789 -0
- package/dist/src/ink/repl-app.js.map +1 -0
- package/dist/src/ink/transcript-cards.d.ts +6 -0
- package/dist/src/ink/transcript-cards.js +24 -0
- package/dist/src/ink/transcript-cards.js.map +1 -0
- package/dist/src/path-policy.d.ts +11 -0
- package/dist/src/path-policy.js +67 -0
- package/dist/src/path-policy.js.map +1 -0
- package/dist/src/process-utils.d.ts +13 -0
- package/dist/src/process-utils.js +52 -0
- package/dist/src/process-utils.js.map +1 -0
- package/dist/src/repl.d.ts +9 -0
- package/dist/src/repl.js +13 -0
- package/dist/src/repl.js.map +1 -0
- package/dist/src/sarvam/client.d.ts +15 -0
- package/dist/src/sarvam/client.js +208 -0
- package/dist/src/sarvam/client.js.map +1 -0
- package/dist/src/sarvam/models.d.ts +2 -0
- package/dist/src/sarvam/models.js +7 -0
- package/dist/src/sarvam/models.js.map +1 -0
- package/dist/src/search-provider.d.ts +6 -0
- package/dist/src/search-provider.js +8 -0
- package/dist/src/search-provider.js.map +1 -0
- package/dist/src/session-store.d.ts +19 -0
- package/dist/src/session-store.js +318 -0
- package/dist/src/session-store.js.map +1 -0
- package/dist/src/skills/runtime.d.ts +26 -0
- package/dist/src/skills/runtime.js +317 -0
- package/dist/src/skills/runtime.js.map +1 -0
- package/dist/src/skills/types.d.ts +25 -0
- package/dist/src/skills/types.js +2 -0
- package/dist/src/skills/types.js.map +1 -0
- package/dist/src/terminal-ui.d.ts +29 -0
- package/dist/src/terminal-ui.js +236 -0
- package/dist/src/terminal-ui.js.map +1 -0
- package/dist/src/tools/filesystem.d.ts +2 -0
- package/dist/src/tools/filesystem.js +622 -0
- package/dist/src/tools/filesystem.js.map +1 -0
- package/dist/src/tools/git.d.ts +2 -0
- package/dist/src/tools/git.js +326 -0
- package/dist/src/tools/git.js.map +1 -0
- package/dist/src/tools/index.d.ts +6 -0
- package/dist/src/tools/index.js +21 -0
- package/dist/src/tools/index.js.map +1 -0
- package/dist/src/tools/registry.d.ts +15 -0
- package/dist/src/tools/registry.js +59 -0
- package/dist/src/tools/registry.js.map +1 -0
- package/dist/src/tools/shell.d.ts +2 -0
- package/dist/src/tools/shell.js +97 -0
- package/dist/src/tools/shell.js.map +1 -0
- package/dist/src/tools/skill.d.ts +3 -0
- package/dist/src/tools/skill.js +130 -0
- package/dist/src/tools/skill.js.map +1 -0
- package/dist/src/tools/web.d.ts +3 -0
- package/dist/src/tools/web.js +144 -0
- package/dist/src/tools/web.js.map +1 -0
- package/dist/src/types.d.ts +236 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/workspace-trust.d.ts +3 -0
- package/dist/src/workspace-trust.js +31 -0
- package/dist/src/workspace-trust.js.map +1 -0
- package/dist/src/xdg.d.ts +9 -0
- package/dist/src/xdg.js +77 -0
- package/dist/src/xdg.js.map +1 -0
- package/package.json +57 -0
- package/skill/agents-md-generator/SKILL.md +75 -0
- package/skill/agents-md-generator/references/agents_md_template.md +160 -0
- package/skill/agents-md-generator/references/loc_measurement.md +67 -0
- package/skill/agents-md-generator/references/monorepo_detection.md +78 -0
- package/skill/agents-md-generator/references/monorepo_strategy.md +60 -0
- package/skill/agents-md-generator/references/read_only_commands.md +151 -0
- package/skill/agents-md-generator/references/update_strategy.md +160 -0
- package/skill/agents-md-generator/references/working_agreements.md +53 -0
- package/skill/biz-opportunity-scout/SKILL.md +53 -0
- package/skill/biz-opportunity-scout/references/competitive_analysis.md +84 -0
- package/skill/biz-opportunity-scout/references/market_sizing.md +68 -0
- package/skill/biz-opportunity-scout/references/pmf_indicators.md +94 -0
- package/skill/biz-opportunity-scout/references/report_template.md +243 -0
- package/skill/biz-opportunity-scout/references/unit_economics.md +97 -0
- package/skill/code-review/SKILL.md +86 -0
- package/skill/code-review/references/change_analysis.md +116 -0
- package/skill/code-review/references/git_operations.md +115 -0
- package/skill/code-review/references/impact_detection.md +149 -0
- package/skill/code-review/references/output_format.md +137 -0
- package/skill/code-review/references/severity_criteria.md +100 -0
- package/skill/code-security-audit/SKILL.md +123 -0
- package/skill/code-security-audit/references/audit_process.md +277 -0
- package/skill/code-security-audit/references/remediation_patterns.md +599 -0
- package/skill/code-security-audit/references/report_format.md +391 -0
- package/skill/code-security-audit/references/security_domains.md +830 -0
- package/skill/code-security-audit/references/vulnerability_patterns.md +813 -0
- package/skill/composition-patterns/SKILL.md +83 -0
- package/skill/composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
- package/skill/composition-patterns/rules/architecture-compound-components.md +112 -0
- package/skill/composition-patterns/rules/patterns-children-over-render-props.md +87 -0
- package/skill/composition-patterns/rules/patterns-explicit-variants.md +100 -0
- package/skill/composition-patterns/rules/react19-no-forwardref.md +42 -0
- package/skill/composition-patterns/rules/state-context-interface.md +191 -0
- package/skill/composition-patterns/rules/state-decouple-implementation.md +113 -0
- package/skill/composition-patterns/rules/state-lift-state.md +125 -0
- package/skill/deploy-to-vercel/SKILL.md +293 -0
- package/skill/deploy-to-vercel/resources/deploy-sandbox.sh +301 -0
- package/skill/deploy-to-vercel/resources/deploy.sh +301 -0
- package/skill/doc/SKILL_GUIDELINES.md +138 -0
- package/skill/git-workflow/SKILL.md +94 -0
- package/skill/git-workflow/references/advanced-git.md +632 -0
- package/skill/git-workflow/references/branching-strategies.md +344 -0
- package/skill/git-workflow/references/ci-cd-integration.md +683 -0
- package/skill/git-workflow/references/code-quality-tools.md +351 -0
- package/skill/git-workflow/references/commit-conventions.md +439 -0
- package/skill/git-workflow/references/github-releases.md +288 -0
- package/skill/git-workflow/references/pull-request-workflow.md +773 -0
- package/skill/git-workflow/scripts/verify-git-workflow.sh +263 -0
- package/skill/jetbrains-vmoptions/SKILL.md +51 -0
- package/skill/jetbrains-vmoptions/references/common-options.md +357 -0
- package/skill/jetbrains-vmoptions/references/gc-options.md +350 -0
- package/skill/jetbrains-vmoptions/references/memory-options.md +339 -0
- package/skill/jetbrains-vmoptions/references/prerequisite-check.md +65 -0
- package/skill/kysely-converter/SKILL.md +62 -0
- package/skill/kysely-converter/references/delete.md +323 -0
- package/skill/kysely-converter/references/insert.md +386 -0
- package/skill/kysely-converter/references/operators.md +331 -0
- package/skill/kysely-converter/references/select.md +1000 -0
- package/skill/kysely-converter/references/update.md +349 -0
- package/skill/kysely-converter/references/window_function.md +537 -0
- package/skill/react-best-practices/SKILL.md +131 -0
- package/skill/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/skill/react-best-practices/rules/advanced-init-once.md +42 -0
- package/skill/react-best-practices/rules/advanced-use-latest.md +39 -0
- package/skill/react-best-practices/rules/async-api-routes.md +38 -0
- package/skill/react-best-practices/rules/async-defer-await.md +80 -0
- package/skill/react-best-practices/rules/async-dependencies.md +51 -0
- package/skill/react-best-practices/rules/async-parallel.md +28 -0
- package/skill/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/skill/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/skill/react-best-practices/rules/bundle-conditional.md +31 -0
- package/skill/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/skill/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/skill/react-best-practices/rules/bundle-preload.md +50 -0
- package/skill/react-best-practices/rules/client-event-listeners.md +74 -0
- package/skill/react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/skill/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/skill/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/skill/react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/skill/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/skill/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/skill/react-best-practices/rules/js-cache-storage.md +70 -0
- package/skill/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/skill/react-best-practices/rules/js-early-exit.md +50 -0
- package/skill/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/skill/react-best-practices/rules/js-index-maps.md +37 -0
- package/skill/react-best-practices/rules/js-length-check-first.md +49 -0
- package/skill/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/skill/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/skill/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/skill/react-best-practices/rules/rendering-activity.md +26 -0
- package/skill/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/skill/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/skill/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/skill/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/skill/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/skill/react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/skill/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/skill/react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/skill/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/skill/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/skill/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/skill/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/skill/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/skill/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/skill/react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/skill/react-best-practices/rules/rerender-memo.md +44 -0
- package/skill/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/skill/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/skill/react-best-practices/rules/rerender-transitions.md +40 -0
- package/skill/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/skill/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/skill/react-best-practices/rules/server-auth-actions.md +96 -0
- package/skill/react-best-practices/rules/server-cache-lru.md +41 -0
- package/skill/react-best-practices/rules/server-cache-react.md +76 -0
- package/skill/react-best-practices/rules/server-dedup-props.md +65 -0
- package/skill/react-best-practices/rules/server-hoist-static-io.md +142 -0
- package/skill/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/skill/react-best-practices/rules/server-serialization.md +38 -0
- package/skill/react-native-skills/SKILL.md +115 -0
- package/skill/react-native-skills/rules/animation-derived-value.md +53 -0
- package/skill/react-native-skills/rules/animation-gesture-detector-press.md +95 -0
- package/skill/react-native-skills/rules/animation-gpu-properties.md +65 -0
- package/skill/react-native-skills/rules/design-system-compound-components.md +66 -0
- package/skill/react-native-skills/rules/fonts-config-plugin.md +71 -0
- package/skill/react-native-skills/rules/imports-design-system-folder.md +68 -0
- package/skill/react-native-skills/rules/js-hoist-intl.md +61 -0
- package/skill/react-native-skills/rules/list-performance-callbacks.md +44 -0
- package/skill/react-native-skills/rules/list-performance-function-references.md +132 -0
- package/skill/react-native-skills/rules/list-performance-images.md +53 -0
- package/skill/react-native-skills/rules/list-performance-inline-objects.md +97 -0
- package/skill/react-native-skills/rules/list-performance-item-expensive.md +94 -0
- package/skill/react-native-skills/rules/list-performance-item-memo.md +82 -0
- package/skill/react-native-skills/rules/list-performance-item-types.md +104 -0
- package/skill/react-native-skills/rules/list-performance-virtualize.md +67 -0
- package/skill/react-native-skills/rules/monorepo-native-deps-in-app.md +46 -0
- package/skill/react-native-skills/rules/monorepo-single-dependency-versions.md +63 -0
- package/skill/react-native-skills/rules/navigation-native-navigators.md +188 -0
- package/skill/react-native-skills/rules/react-compiler-destructure-functions.md +50 -0
- package/skill/react-native-skills/rules/react-compiler-reanimated-shared-values.md +48 -0
- package/skill/react-native-skills/rules/react-state-dispatcher.md +91 -0
- package/skill/react-native-skills/rules/react-state-fallback.md +56 -0
- package/skill/react-native-skills/rules/react-state-minimize.md +65 -0
- package/skill/react-native-skills/rules/rendering-no-falsy-and.md +74 -0
- package/skill/react-native-skills/rules/rendering-text-in-text-component.md +36 -0
- package/skill/react-native-skills/rules/scroll-position-no-state.md +82 -0
- package/skill/react-native-skills/rules/state-ground-truth.md +80 -0
- package/skill/react-native-skills/rules/ui-expo-image.md +66 -0
- package/skill/react-native-skills/rules/ui-image-gallery.md +104 -0
- package/skill/react-native-skills/rules/ui-measure-views.md +78 -0
- package/skill/react-native-skills/rules/ui-menus.md +174 -0
- package/skill/react-native-skills/rules/ui-native-modals.md +77 -0
- package/skill/react-native-skills/rules/ui-pressable.md +61 -0
- package/skill/react-native-skills/rules/ui-safe-area-scroll.md +65 -0
- package/skill/react-native-skills/rules/ui-scrollview-content-inset.md +45 -0
- package/skill/react-native-skills/rules/ui-styling.md +87 -0
- package/skill/react-vite-guide/SKILL.md +101 -0
- package/skill/react-vite-guide/references/composition-patterns.md +709 -0
- package/skill/react-vite-guide/references/performance-optimization.md +1222 -0
- package/skill/react-vite-guide/references/vite-specific.md +385 -0
- package/skill/react-vite-guide/references/web-interface.md +146 -0
- package/skill/skill-maker/SKILL.md +52 -0
- package/skill/skill-maker/references/content_spec.md +67 -0
- package/skill/skill-maker/references/frontmatter_spec.md +96 -0
- package/skill/skill-maker/references/input_validation.md +90 -0
- package/skill/skill-maker/references/skill_structure.md +74 -0
- package/skill/system-prompt-creator/SKILL.md +50 -0
- package/skill/system-prompt-creator/references/data_format_selection.md +135 -0
- package/skill/system-prompt-creator/references/multi_prompt_architecture.md +386 -0
- package/skill/system-prompt-creator/references/prompt_structure.md +140 -0
- package/skill/system-prompt-creator/references/quality_criteria.md +83 -0
- package/skill/typst-creator/SKILL.md +51 -0
- package/skill/typst-creator/references/layout.md +401 -0
- package/skill/typst-creator/references/math.md +297 -0
- package/skill/typst-creator/references/scripting.md +237 -0
- package/skill/typst-creator/references/styling.md +217 -0
- package/skill/typst-creator/references/syntax.md +234 -0
- package/skill/web-design-guidelines/SKILL.md +35 -0
- package/terminal.png +0 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Native Navigators for Navigation
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: native performance, platform-appropriate UI
|
|
5
|
+
tags: navigation, react-navigation, expo-router, native-stack, tabs
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Native Navigators for Navigation
|
|
9
|
+
|
|
10
|
+
Always use native navigators instead of JS-based ones. Native navigators use
|
|
11
|
+
platform APIs (UINavigationController on iOS, Fragment on Android) for better
|
|
12
|
+
performance and native behavior.
|
|
13
|
+
|
|
14
|
+
**For stacks:** Use `@react-navigation/native-stack` or expo-router's default
|
|
15
|
+
stack (which uses native-stack). Avoid `@react-navigation/stack`.
|
|
16
|
+
|
|
17
|
+
**For tabs:** Use `react-native-bottom-tabs` (native) or expo-router's native
|
|
18
|
+
tabs. Avoid `@react-navigation/bottom-tabs` when native feel matters.
|
|
19
|
+
|
|
20
|
+
### Stack Navigation
|
|
21
|
+
|
|
22
|
+
**Incorrect (JS stack navigator):**
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { createStackNavigator } from '@react-navigation/stack'
|
|
26
|
+
|
|
27
|
+
const Stack = createStackNavigator()
|
|
28
|
+
|
|
29
|
+
function App() {
|
|
30
|
+
return (
|
|
31
|
+
<Stack.Navigator>
|
|
32
|
+
<Stack.Screen name='Home' component={HomeScreen} />
|
|
33
|
+
<Stack.Screen name='Details' component={DetailsScreen} />
|
|
34
|
+
</Stack.Navigator>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Correct (native stack with react-navigation):**
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import { createNativeStackNavigator } from '@react-navigation/native-stack'
|
|
43
|
+
|
|
44
|
+
const Stack = createNativeStackNavigator()
|
|
45
|
+
|
|
46
|
+
function App() {
|
|
47
|
+
return (
|
|
48
|
+
<Stack.Navigator>
|
|
49
|
+
<Stack.Screen name='Home' component={HomeScreen} />
|
|
50
|
+
<Stack.Screen name='Details' component={DetailsScreen} />
|
|
51
|
+
</Stack.Navigator>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Correct (expo-router uses native stack by default):**
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
// app/_layout.tsx
|
|
60
|
+
import { Stack } from 'expo-router'
|
|
61
|
+
|
|
62
|
+
export default function Layout() {
|
|
63
|
+
return <Stack />
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Tab Navigation
|
|
68
|
+
|
|
69
|
+
**Incorrect (JS bottom tabs):**
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
|
|
73
|
+
|
|
74
|
+
const Tab = createBottomTabNavigator()
|
|
75
|
+
|
|
76
|
+
function App() {
|
|
77
|
+
return (
|
|
78
|
+
<Tab.Navigator>
|
|
79
|
+
<Tab.Screen name='Home' component={HomeScreen} />
|
|
80
|
+
<Tab.Screen name='Settings' component={SettingsScreen} />
|
|
81
|
+
</Tab.Navigator>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Correct (native bottom tabs with react-navigation):**
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
import { createNativeBottomTabNavigator } from '@bottom-tabs/react-navigation'
|
|
90
|
+
|
|
91
|
+
const Tab = createNativeBottomTabNavigator()
|
|
92
|
+
|
|
93
|
+
function App() {
|
|
94
|
+
return (
|
|
95
|
+
<Tab.Navigator>
|
|
96
|
+
<Tab.Screen
|
|
97
|
+
name='Home'
|
|
98
|
+
component={HomeScreen}
|
|
99
|
+
options={{
|
|
100
|
+
tabBarIcon: () => ({ sfSymbol: 'house' }),
|
|
101
|
+
}}
|
|
102
|
+
/>
|
|
103
|
+
<Tab.Screen
|
|
104
|
+
name='Settings'
|
|
105
|
+
component={SettingsScreen}
|
|
106
|
+
options={{
|
|
107
|
+
tabBarIcon: () => ({ sfSymbol: 'gear' }),
|
|
108
|
+
}}
|
|
109
|
+
/>
|
|
110
|
+
</Tab.Navigator>
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Correct (expo-router native tabs):**
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
// app/(tabs)/_layout.tsx
|
|
119
|
+
import { NativeTabs } from 'expo-router/unstable-native-tabs'
|
|
120
|
+
|
|
121
|
+
export default function TabLayout() {
|
|
122
|
+
return (
|
|
123
|
+
<NativeTabs>
|
|
124
|
+
<NativeTabs.Trigger name='index'>
|
|
125
|
+
<NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label>
|
|
126
|
+
<NativeTabs.Trigger.Icon sf='house.fill' md='home' />
|
|
127
|
+
</NativeTabs.Trigger>
|
|
128
|
+
<NativeTabs.Trigger name='settings'>
|
|
129
|
+
<NativeTabs.Trigger.Label>Settings</NativeTabs.Trigger.Label>
|
|
130
|
+
<NativeTabs.Trigger.Icon sf='gear' md='settings' />
|
|
131
|
+
</NativeTabs.Trigger>
|
|
132
|
+
</NativeTabs>
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
On iOS, native tabs automatically enable `contentInsetAdjustmentBehavior` on the
|
|
138
|
+
first `ScrollView` at the root of each tab screen, so content scrolls correctly
|
|
139
|
+
behind the translucent tab bar. If you need to disable this, use
|
|
140
|
+
`disableAutomaticContentInsets` on the trigger.
|
|
141
|
+
|
|
142
|
+
### Prefer Native Header Options Over Custom Components
|
|
143
|
+
|
|
144
|
+
**Incorrect (custom header component):**
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
<Stack.Screen
|
|
148
|
+
name='Profile'
|
|
149
|
+
component={ProfileScreen}
|
|
150
|
+
options={{
|
|
151
|
+
header: () => <CustomHeader title='Profile' />,
|
|
152
|
+
}}
|
|
153
|
+
/>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Correct (native header options):**
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
<Stack.Screen
|
|
160
|
+
name='Profile'
|
|
161
|
+
component={ProfileScreen}
|
|
162
|
+
options={{
|
|
163
|
+
title: 'Profile',
|
|
164
|
+
headerLargeTitleEnabled: true,
|
|
165
|
+
headerSearchBarOptions: {
|
|
166
|
+
placeholder: 'Search',
|
|
167
|
+
},
|
|
168
|
+
}}
|
|
169
|
+
/>
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Native headers support iOS large titles, search bars, blur effects, and proper
|
|
173
|
+
safe area handling automatically.
|
|
174
|
+
|
|
175
|
+
### Why Native Navigators
|
|
176
|
+
|
|
177
|
+
- **Performance**: Native transitions and gestures run on the UI thread
|
|
178
|
+
- **Platform behavior**: Automatic iOS large titles, Android material design
|
|
179
|
+
- **System integration**: Scroll-to-top on tab tap, PiP avoidance, proper safe
|
|
180
|
+
areas
|
|
181
|
+
- **Accessibility**: Platform accessibility features work automatically
|
|
182
|
+
|
|
183
|
+
Reference:
|
|
184
|
+
|
|
185
|
+
- [React Navigation Native Stack](https://reactnavigation.org/docs/native-stack-navigator)
|
|
186
|
+
- [React Native Bottom Tabs with React Navigation](https://oss.callstack.com/react-native-bottom-tabs/docs/guides/usage-with-react-navigation)
|
|
187
|
+
- [React Native Bottom Tabs with Expo Router](https://oss.callstack.com/react-native-bottom-tabs/docs/guides/usage-with-expo-router)
|
|
188
|
+
- [Expo Router Native Tabs](https://docs.expo.dev/router/advanced/native-tabs)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Destructure Functions Early in Render (React Compiler)
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: stable references, fewer re-renders
|
|
5
|
+
tags: rerender, hooks, performance, react-compiler
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Destructure Functions Early in Render
|
|
9
|
+
|
|
10
|
+
This rule is only applicable if you are using the React Compiler.
|
|
11
|
+
|
|
12
|
+
Destructure functions from hooks at the top of render scope. Never dot into
|
|
13
|
+
objects to call functions. Destructured functions are stable references; dotting
|
|
14
|
+
creates new references and breaks memoization.
|
|
15
|
+
|
|
16
|
+
**Incorrect (dotting into object):**
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
import { useRouter } from 'expo-router'
|
|
20
|
+
|
|
21
|
+
function SaveButton(props) {
|
|
22
|
+
const router = useRouter()
|
|
23
|
+
|
|
24
|
+
// bad: react-compiler will key the cache on "props" and "router", which are objects that change each render
|
|
25
|
+
const handlePress = () => {
|
|
26
|
+
props.onSave()
|
|
27
|
+
router.push('/success') // unstable reference
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return <Button onPress={handlePress}>Save</Button>
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Correct (destructure early):**
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import { useRouter } from 'expo-router'
|
|
38
|
+
|
|
39
|
+
function SaveButton({ onSave }) {
|
|
40
|
+
const { push } = useRouter()
|
|
41
|
+
|
|
42
|
+
// good: react-compiler will key on push and onSave
|
|
43
|
+
const handlePress = () => {
|
|
44
|
+
onSave()
|
|
45
|
+
push('/success') // stable reference
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return <Button onPress={handlePress}>Save</Button>
|
|
49
|
+
}
|
|
50
|
+
```
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use .get() and .set() for Reanimated Shared Values (not .value)
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: required for React Compiler compatibility
|
|
5
|
+
tags: reanimated, react-compiler, shared-values
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use .get() and .set() for Shared Values with React Compiler
|
|
9
|
+
|
|
10
|
+
With React Compiler enabled, use `.get()` and `.set()` instead of reading or
|
|
11
|
+
writing `.value` directly on Reanimated shared values. The compiler can't track
|
|
12
|
+
property access—explicit methods ensure correct behavior.
|
|
13
|
+
|
|
14
|
+
**Incorrect (breaks with React Compiler):**
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { useSharedValue } from 'react-native-reanimated'
|
|
18
|
+
|
|
19
|
+
function Counter() {
|
|
20
|
+
const count = useSharedValue(0)
|
|
21
|
+
|
|
22
|
+
const increment = () => {
|
|
23
|
+
count.value = count.value + 1 // opts out of react compiler
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return <Button onPress={increment} title={`Count: ${count.value}`} />
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Correct (React Compiler compatible):**
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
import { useSharedValue } from 'react-native-reanimated'
|
|
34
|
+
|
|
35
|
+
function Counter() {
|
|
36
|
+
const count = useSharedValue(0)
|
|
37
|
+
|
|
38
|
+
const increment = () => {
|
|
39
|
+
count.set(count.get() + 1)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return <Button onPress={increment} title={`Count: ${count.get()}`} />
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
See the
|
|
47
|
+
[Reanimated docs](https://docs.swmansion.com/react-native-reanimated/docs/core/useSharedValue/#react-compiler-support)
|
|
48
|
+
for more.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: useState Dispatch updaters for State That Depends on Current Value
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: avoids stale closures, prevents unnecessary re-renders
|
|
5
|
+
tags: state, hooks, useState, callbacks
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Dispatch Updaters for State That Depends on Current Value
|
|
9
|
+
|
|
10
|
+
When the next state depends on the current state, use a dispatch updater
|
|
11
|
+
(`setState(prev => ...)`) instead of reading the state variable directly in a
|
|
12
|
+
callback. This avoids stale closures and ensures you're comparing against the
|
|
13
|
+
latest value.
|
|
14
|
+
|
|
15
|
+
**Incorrect (reads state directly):**
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
const [size, setSize] = useState<Size | undefined>(undefined)
|
|
19
|
+
|
|
20
|
+
const onLayout = (e: LayoutChangeEvent) => {
|
|
21
|
+
const { width, height } = e.nativeEvent.layout
|
|
22
|
+
// size may be stale in this closure
|
|
23
|
+
if (size?.width !== width || size?.height !== height) {
|
|
24
|
+
setSize({ width, height })
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Correct (dispatch updater):**
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
const [size, setSize] = useState<Size | undefined>(undefined)
|
|
33
|
+
|
|
34
|
+
const onLayout = (e: LayoutChangeEvent) => {
|
|
35
|
+
const { width, height } = e.nativeEvent.layout
|
|
36
|
+
setSize((prev) => {
|
|
37
|
+
if (prev?.width === width && prev?.height === height) return prev
|
|
38
|
+
return { width, height }
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Returning the previous value from the updater skips the re-render.
|
|
44
|
+
|
|
45
|
+
For primitive states, you don't need to compare values before firing a
|
|
46
|
+
re-render.
|
|
47
|
+
|
|
48
|
+
**Incorrect (unnecessary comparison for primitive state):**
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
const [size, setSize] = useState<Size | undefined>(undefined)
|
|
52
|
+
|
|
53
|
+
const onLayout = (e: LayoutChangeEvent) => {
|
|
54
|
+
const { width, height } = e.nativeEvent.layout
|
|
55
|
+
setSize((prev) => (prev === width ? prev : width))
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Correct (sets primitive state directly):**
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
const [size, setSize] = useState<Size | undefined>(undefined)
|
|
63
|
+
|
|
64
|
+
const onLayout = (e: LayoutChangeEvent) => {
|
|
65
|
+
const { width, height } = e.nativeEvent.layout
|
|
66
|
+
setSize(width)
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
However, if the next state depends on the current state, you should still use a
|
|
71
|
+
dispatch updater.
|
|
72
|
+
|
|
73
|
+
**Incorrect (reads state directly from the callback):**
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
const [count, setCount] = useState(0)
|
|
77
|
+
|
|
78
|
+
const onTap = () => {
|
|
79
|
+
setCount(count + 1)
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Correct (dispatch updater):**
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
const [count, setCount] = useState(0)
|
|
87
|
+
|
|
88
|
+
const onTap = () => {
|
|
89
|
+
setCount((prev) => prev + 1)
|
|
90
|
+
}
|
|
91
|
+
```
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use fallback state instead of initialState
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: reactive fallbacks without syncing
|
|
5
|
+
tags: state, hooks, derived-state, props, initialState
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use fallback state instead of initialState
|
|
9
|
+
|
|
10
|
+
Use `undefined` as initial state and nullish coalescing (`??`) to fall back to
|
|
11
|
+
parent or server values. State represents user intent only—`undefined` means
|
|
12
|
+
"user hasn't chosen yet." This enables reactive fallbacks that update when the
|
|
13
|
+
source changes, not just on initial render.
|
|
14
|
+
|
|
15
|
+
**Incorrect (syncs state, loses reactivity):**
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
type Props = { fallbackEnabled: boolean }
|
|
19
|
+
|
|
20
|
+
function Toggle({ fallbackEnabled }: Props) {
|
|
21
|
+
const [enabled, setEnabled] = useState(defaultEnabled)
|
|
22
|
+
// If fallbackEnabled changes, state is stale
|
|
23
|
+
// State mixes user intent with default value
|
|
24
|
+
|
|
25
|
+
return <Switch value={enabled} onValueChange={setEnabled} />
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Correct (state is user intent, reactive fallback):**
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
type Props = { fallbackEnabled: boolean }
|
|
33
|
+
|
|
34
|
+
function Toggle({ fallbackEnabled }: Props) {
|
|
35
|
+
const [_enabled, setEnabled] = useState<boolean | undefined>(undefined)
|
|
36
|
+
const enabled = _enabled ?? defaultEnabled
|
|
37
|
+
// undefined = user hasn't touched it, falls back to prop
|
|
38
|
+
// If defaultEnabled changes, component reflects it
|
|
39
|
+
// Once user interacts, their choice persists
|
|
40
|
+
|
|
41
|
+
return <Switch value={enabled} onValueChange={setEnabled} />
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**With server data:**
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
function ProfileForm({ data }: { data: User }) {
|
|
49
|
+
const [_theme, setTheme] = useState<string | undefined>(undefined)
|
|
50
|
+
const theme = _theme ?? data.theme
|
|
51
|
+
// Shows server value until user overrides
|
|
52
|
+
// Server refetch updates the fallback automatically
|
|
53
|
+
|
|
54
|
+
return <ThemePicker value={theme} onChange={setTheme} />
|
|
55
|
+
}
|
|
56
|
+
```
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Minimize State Variables and Derive Values
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: fewer re-renders, less state drift
|
|
5
|
+
tags: state, derived-state, hooks, optimization
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Minimize State Variables and Derive Values
|
|
9
|
+
|
|
10
|
+
Use the fewest state variables possible. If a value can be computed from existing state or props, derive it during render instead of storing it in state. Redundant state causes unnecessary re-renders and can drift out of sync.
|
|
11
|
+
|
|
12
|
+
**Incorrect (redundant state):**
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
function Cart({ items }: { items: Item[] }) {
|
|
16
|
+
const [total, setTotal] = useState(0)
|
|
17
|
+
const [itemCount, setItemCount] = useState(0)
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
setTotal(items.reduce((sum, item) => sum + item.price, 0))
|
|
21
|
+
setItemCount(items.length)
|
|
22
|
+
}, [items])
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<View>
|
|
26
|
+
<Text>{itemCount} items</Text>
|
|
27
|
+
<Text>Total: ${total}</Text>
|
|
28
|
+
</View>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Correct (derived values):**
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
function Cart({ items }: { items: Item[] }) {
|
|
37
|
+
const total = items.reduce((sum, item) => sum + item.price, 0)
|
|
38
|
+
const itemCount = items.length
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<View>
|
|
42
|
+
<Text>{itemCount} items</Text>
|
|
43
|
+
<Text>Total: ${total}</Text>
|
|
44
|
+
</View>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Another example:**
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
// Incorrect: storing both firstName, lastName, AND fullName
|
|
53
|
+
const [firstName, setFirstName] = useState('')
|
|
54
|
+
const [lastName, setLastName] = useState('')
|
|
55
|
+
const [fullName, setFullName] = useState('')
|
|
56
|
+
|
|
57
|
+
// Correct: derive fullName
|
|
58
|
+
const [firstName, setFirstName] = useState('')
|
|
59
|
+
const [lastName, setLastName] = useState('')
|
|
60
|
+
const fullName = `${firstName} ${lastName}`
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
State should be the minimal source of truth. Everything else is derived.
|
|
64
|
+
|
|
65
|
+
Reference: [Choosing the State Structure](https://react.dev/learn/choosing-the-state-structure)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Never Use && with Potentially Falsy Values
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: prevents production crash
|
|
5
|
+
tags: rendering, conditional, jsx, crash
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Never Use && with Potentially Falsy Values
|
|
9
|
+
|
|
10
|
+
Never use `{value && <Component />}` when `value` could be an empty string or
|
|
11
|
+
`0`. These are falsy but JSX-renderable—React Native will try to render them as
|
|
12
|
+
text outside a `<Text>` component, causing a hard crash in production.
|
|
13
|
+
|
|
14
|
+
**Incorrect (crashes if count is 0 or name is ""):**
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
function Profile({ name, count }: { name: string; count: number }) {
|
|
18
|
+
return (
|
|
19
|
+
<View>
|
|
20
|
+
{name && <Text>{name}</Text>}
|
|
21
|
+
{count && <Text>{count} items</Text>}
|
|
22
|
+
</View>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
// If name="" or count=0, renders the falsy value → crash
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Correct (ternary with null):**
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
function Profile({ name, count }: { name: string; count: number }) {
|
|
32
|
+
return (
|
|
33
|
+
<View>
|
|
34
|
+
{name ? <Text>{name}</Text> : null}
|
|
35
|
+
{count ? <Text>{count} items</Text> : null}
|
|
36
|
+
</View>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Correct (explicit boolean coercion):**
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
function Profile({ name, count }: { name: string; count: number }) {
|
|
45
|
+
return (
|
|
46
|
+
<View>
|
|
47
|
+
{!!name && <Text>{name}</Text>}
|
|
48
|
+
{!!count && <Text>{count} items</Text>}
|
|
49
|
+
</View>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Best (early return):**
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
function Profile({ name, count }: { name: string; count: number }) {
|
|
58
|
+
if (!name) return null
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<View>
|
|
62
|
+
<Text>{name}</Text>
|
|
63
|
+
{count > 0 ? <Text>{count} items</Text> : null}
|
|
64
|
+
</View>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Early returns are clearest. When using conditionals inline, prefer ternary or
|
|
70
|
+
explicit boolean checks.
|
|
71
|
+
|
|
72
|
+
**Lint rule:** Enable `react/jsx-no-leaked-render` from
|
|
73
|
+
[eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-no-leaked-render.md)
|
|
74
|
+
to catch this automatically.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Wrap Strings in Text Components
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: prevents runtime crash
|
|
5
|
+
tags: rendering, text, core
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Wrap Strings in Text Components
|
|
9
|
+
|
|
10
|
+
Strings must be rendered inside `<Text>`. React Native crashes if a string is a
|
|
11
|
+
direct child of `<View>`.
|
|
12
|
+
|
|
13
|
+
**Incorrect (crashes):**
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { View } from 'react-native'
|
|
17
|
+
|
|
18
|
+
function Greeting({ name }: { name: string }) {
|
|
19
|
+
return <View>Hello, {name}!</View>
|
|
20
|
+
}
|
|
21
|
+
// Error: Text strings must be rendered within a <Text> component.
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Correct:**
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import { View, Text } from 'react-native'
|
|
28
|
+
|
|
29
|
+
function Greeting({ name }: { name: string }) {
|
|
30
|
+
return (
|
|
31
|
+
<View>
|
|
32
|
+
<Text>Hello, {name}!</Text>
|
|
33
|
+
</View>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
```
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Never Track Scroll Position in useState
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: prevents render thrashing during scroll
|
|
5
|
+
tags: scroll, performance, reanimated, useRef
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Never Track Scroll Position in useState
|
|
9
|
+
|
|
10
|
+
Never store scroll position in `useState`. Scroll events fire rapidly—state
|
|
11
|
+
updates cause render thrashing and dropped frames. Use a Reanimated shared value
|
|
12
|
+
for animations or a ref for non-reactive tracking.
|
|
13
|
+
|
|
14
|
+
**Incorrect (useState causes jank):**
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { useState } from 'react'
|
|
18
|
+
import {
|
|
19
|
+
ScrollView,
|
|
20
|
+
NativeSyntheticEvent,
|
|
21
|
+
NativeScrollEvent,
|
|
22
|
+
} from 'react-native'
|
|
23
|
+
|
|
24
|
+
function Feed() {
|
|
25
|
+
const [scrollY, setScrollY] = useState(0)
|
|
26
|
+
|
|
27
|
+
const onScroll = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
28
|
+
setScrollY(e.nativeEvent.contentOffset.y) // re-renders on every frame
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return <ScrollView onScroll={onScroll} scrollEventThrottle={16} />
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Correct (Reanimated for animations):**
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import Animated, {
|
|
39
|
+
useSharedValue,
|
|
40
|
+
useAnimatedScrollHandler,
|
|
41
|
+
} from 'react-native-reanimated'
|
|
42
|
+
|
|
43
|
+
function Feed() {
|
|
44
|
+
const scrollY = useSharedValue(0)
|
|
45
|
+
|
|
46
|
+
const onScroll = useAnimatedScrollHandler({
|
|
47
|
+
onScroll: (e) => {
|
|
48
|
+
scrollY.value = e.contentOffset.y // runs on UI thread, no re-render
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<Animated.ScrollView
|
|
54
|
+
onScroll={onScroll}
|
|
55
|
+
// higher number has better performance, but it fires less often.
|
|
56
|
+
// unset this if you need higher precision over performance.
|
|
57
|
+
scrollEventThrottle={16}
|
|
58
|
+
/>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Correct (ref for non-reactive tracking):**
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { useRef } from 'react'
|
|
67
|
+
import {
|
|
68
|
+
ScrollView,
|
|
69
|
+
NativeSyntheticEvent,
|
|
70
|
+
NativeScrollEvent,
|
|
71
|
+
} from 'react-native'
|
|
72
|
+
|
|
73
|
+
function Feed() {
|
|
74
|
+
const scrollY = useRef(0)
|
|
75
|
+
|
|
76
|
+
const onScroll = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
77
|
+
scrollY.current = e.nativeEvent.contentOffset.y // no re-render
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return <ScrollView onScroll={onScroll} scrollEventThrottle={16} />
|
|
81
|
+
}
|
|
82
|
+
```
|