devskill 2.0.2
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.
- package/README.md +123 -0
- package/README.vn.md +104 -0
- package/SKILL_GUIDE.vn.md +252 -0
- package/bin/devskill.js +11 -0
- package/meta.ts +123 -0
- package/package.json +21 -0
- package/publish.sh +50 -0
- package/scripts/cli.ts +556 -0
- package/skills/builderx_api-contexts/SKILL.md +63 -0
- package/skills/builderx_api-contexts/references/core-multi-outbox.md +57 -0
- package/skills/builderx_api-controllers/SKILL.md +49 -0
- package/skills/builderx_api-controllers/references/core-fallback.md +62 -0
- package/skills/builderx_api-schemas/SKILL.md +76 -0
- package/skills/builderx_api-schemas/references/core-schema.md +78 -0
- package/skills/builderx_spa-api/SKILL.md +67 -0
- package/skills/builderx_spa-api/references/base-api.md +97 -0
- package/skills/builderx_spa-api/references/fetch.md +70 -0
- package/skills/builderx_spa-design/SKILL.md +82 -0
- package/skills/builderx_spa-design/references/core-text-design.md +64 -0
- package/skills/builderx_spa-design/references/features-feedback-actions.md +76 -0
- package/skills/builderx_spa-design/references/features-forms.md +91 -0
- package/skills/builderx_spa-design/references/features-layout-navigation.md +121 -0
- package/skills/builderx_spa-permission/SKILL.md +38 -0
- package/skills/builderx_spa-permission/references/has-permission.md +34 -0
- package/skills/composition-patterns/AGENTS.md +946 -0
- package/skills/composition-patterns/README.md +60 -0
- package/skills/composition-patterns/SKILL.md +89 -0
- package/skills/composition-patterns/SYNC.md +5 -0
- package/skills/composition-patterns/metadata.json +11 -0
- package/skills/composition-patterns/rules/_sections.md +29 -0
- package/skills/composition-patterns/rules/_template.md +24 -0
- package/skills/composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
- package/skills/composition-patterns/rules/architecture-compound-components.md +112 -0
- package/skills/composition-patterns/rules/patterns-children-over-render-props.md +87 -0
- package/skills/composition-patterns/rules/patterns-explicit-variants.md +100 -0
- package/skills/composition-patterns/rules/react19-no-forwardref.md +42 -0
- package/skills/composition-patterns/rules/state-context-interface.md +191 -0
- package/skills/composition-patterns/rules/state-decouple-implementation.md +113 -0
- package/skills/composition-patterns/rules/state-lift-state.md +125 -0
- package/skills/deploy-to-vercel/Archive.zip +0 -0
- package/skills/deploy-to-vercel/SKILL.md +296 -0
- package/skills/deploy-to-vercel/SYNC.md +5 -0
- package/skills/deploy-to-vercel/resources/deploy-codex.sh +301 -0
- package/skills/deploy-to-vercel/resources/deploy.sh +301 -0
- package/skills/pinia/SKILL.md +105 -0
- package/skills/pinia/references/api-in-composables.md +139 -0
- package/skills/pinia/references/setup-stores.md +105 -0
- package/skills/pinia/references/using-in-components.md +91 -0
- package/skills/pinia-options/SKILL.md +72 -0
- package/skills/pinia-options/references/core-option-stores.md +149 -0
- package/skills/pinia-options/references/using-in-options-api.md +105 -0
- package/skills/react-best-practices/AGENTS.md +3373 -0
- package/skills/react-best-practices/README.md +123 -0
- package/skills/react-best-practices/SKILL.md +143 -0
- package/skills/react-best-practices/SYNC.md +5 -0
- package/skills/react-best-practices/metadata.json +15 -0
- package/skills/react-best-practices/rules/_sections.md +46 -0
- package/skills/react-best-practices/rules/_template.md +28 -0
- package/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/skills/react-best-practices/rules/advanced-init-once.md +42 -0
- package/skills/react-best-practices/rules/advanced-use-latest.md +39 -0
- package/skills/react-best-practices/rules/async-api-routes.md +38 -0
- package/skills/react-best-practices/rules/async-defer-await.md +80 -0
- package/skills/react-best-practices/rules/async-dependencies.md +51 -0
- package/skills/react-best-practices/rules/async-parallel.md +28 -0
- package/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/skills/react-best-practices/rules/bundle-conditional.md +31 -0
- package/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/skills/react-best-practices/rules/bundle-preload.md +50 -0
- package/skills/react-best-practices/rules/client-event-listeners.md +74 -0
- package/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/skills/react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/skills/react-best-practices/rules/js-cache-storage.md +70 -0
- package/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/skills/react-best-practices/rules/js-early-exit.md +50 -0
- package/skills/react-best-practices/rules/js-flatmap-filter.md +60 -0
- package/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/skills/react-best-practices/rules/js-index-maps.md +37 -0
- package/skills/react-best-practices/rules/js-length-check-first.md +49 -0
- package/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/skills/react-best-practices/rules/rendering-activity.md +26 -0
- package/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/skills/react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/skills/react-best-practices/rules/rendering-resource-hints.md +85 -0
- package/skills/react-best-practices/rules/rendering-script-defer-async.md +68 -0
- package/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/skills/react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/skills/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/skills/react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/skills/react-best-practices/rules/rerender-memo.md +44 -0
- package/skills/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/skills/react-best-practices/rules/rerender-no-inline-components.md +82 -0
- package/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/skills/react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
- package/skills/react-best-practices/rules/rerender-transitions.md +40 -0
- package/skills/react-best-practices/rules/rerender-use-deferred-value.md +59 -0
- package/skills/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/skills/react-best-practices/rules/server-auth-actions.md +96 -0
- package/skills/react-best-practices/rules/server-cache-lru.md +41 -0
- package/skills/react-best-practices/rules/server-cache-react.md +76 -0
- package/skills/react-best-practices/rules/server-dedup-props.md +65 -0
- package/skills/react-best-practices/rules/server-hoist-static-io.md +142 -0
- package/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/skills/react-best-practices/rules/server-serialization.md +38 -0
- package/skills/react-native-skills/AGENTS.md +2897 -0
- package/skills/react-native-skills/README.md +165 -0
- package/skills/react-native-skills/SKILL.md +121 -0
- package/skills/react-native-skills/SYNC.md +5 -0
- package/skills/react-native-skills/metadata.json +16 -0
- package/skills/react-native-skills/rules/_sections.md +86 -0
- package/skills/react-native-skills/rules/_template.md +28 -0
- package/skills/react-native-skills/rules/animation-derived-value.md +53 -0
- package/skills/react-native-skills/rules/animation-gesture-detector-press.md +95 -0
- package/skills/react-native-skills/rules/animation-gpu-properties.md +65 -0
- package/skills/react-native-skills/rules/design-system-compound-components.md +66 -0
- package/skills/react-native-skills/rules/fonts-config-plugin.md +71 -0
- package/skills/react-native-skills/rules/imports-design-system-folder.md +68 -0
- package/skills/react-native-skills/rules/js-hoist-intl.md +61 -0
- package/skills/react-native-skills/rules/list-performance-callbacks.md +44 -0
- package/skills/react-native-skills/rules/list-performance-function-references.md +132 -0
- package/skills/react-native-skills/rules/list-performance-images.md +53 -0
- package/skills/react-native-skills/rules/list-performance-inline-objects.md +97 -0
- package/skills/react-native-skills/rules/list-performance-item-expensive.md +94 -0
- package/skills/react-native-skills/rules/list-performance-item-memo.md +82 -0
- package/skills/react-native-skills/rules/list-performance-item-types.md +104 -0
- package/skills/react-native-skills/rules/list-performance-virtualize.md +67 -0
- package/skills/react-native-skills/rules/monorepo-native-deps-in-app.md +46 -0
- package/skills/react-native-skills/rules/monorepo-single-dependency-versions.md +63 -0
- package/skills/react-native-skills/rules/navigation-native-navigators.md +188 -0
- package/skills/react-native-skills/rules/react-compiler-destructure-functions.md +50 -0
- package/skills/react-native-skills/rules/react-compiler-reanimated-shared-values.md +48 -0
- package/skills/react-native-skills/rules/react-state-dispatcher.md +91 -0
- package/skills/react-native-skills/rules/react-state-fallback.md +56 -0
- package/skills/react-native-skills/rules/react-state-minimize.md +65 -0
- package/skills/react-native-skills/rules/rendering-no-falsy-and.md +74 -0
- package/skills/react-native-skills/rules/rendering-text-in-text-component.md +36 -0
- package/skills/react-native-skills/rules/scroll-position-no-state.md +82 -0
- package/skills/react-native-skills/rules/state-ground-truth.md +80 -0
- package/skills/react-native-skills/rules/ui-expo-image.md +66 -0
- package/skills/react-native-skills/rules/ui-image-gallery.md +104 -0
- package/skills/react-native-skills/rules/ui-measure-views.md +78 -0
- package/skills/react-native-skills/rules/ui-menus.md +174 -0
- package/skills/react-native-skills/rules/ui-native-modals.md +77 -0
- package/skills/react-native-skills/rules/ui-pressable.md +61 -0
- package/skills/react-native-skills/rules/ui-safe-area-scroll.md +65 -0
- package/skills/react-native-skills/rules/ui-scrollview-content-inset.md +45 -0
- package/skills/react-native-skills/rules/ui-styling.md +87 -0
- package/skills/slidev/LICENSE.md +21 -0
- package/skills/slidev/README.md +61 -0
- package/skills/slidev/SKILL.md +189 -0
- package/skills/slidev/SYNC.md +5 -0
- package/skills/slidev/references/animation-click-marker.md +37 -0
- package/skills/slidev/references/animation-drawing.md +68 -0
- package/skills/slidev/references/animation-rough-marker.md +53 -0
- package/skills/slidev/references/api-slide-hooks.md +37 -0
- package/skills/slidev/references/build-og-image.md +36 -0
- package/skills/slidev/references/build-pdf.md +40 -0
- package/skills/slidev/references/build-remote-assets.md +34 -0
- package/skills/slidev/references/build-seo-meta.md +43 -0
- package/skills/slidev/references/code-groups.md +64 -0
- package/skills/slidev/references/code-import-snippet.md +55 -0
- package/skills/slidev/references/code-line-highlighting.md +50 -0
- package/skills/slidev/references/code-line-numbers.md +46 -0
- package/skills/slidev/references/code-magic-move.md +57 -0
- package/skills/slidev/references/code-max-height.md +37 -0
- package/skills/slidev/references/code-twoslash.md +42 -0
- package/skills/slidev/references/core-animations.md +196 -0
- package/skills/slidev/references/core-cli.md +140 -0
- package/skills/slidev/references/core-components.md +197 -0
- package/skills/slidev/references/core-exporting.md +148 -0
- package/skills/slidev/references/core-frontmatter.md +195 -0
- package/skills/slidev/references/core-global-context.md +155 -0
- package/skills/slidev/references/core-headmatter.md +188 -0
- package/skills/slidev/references/core-hosting.md +152 -0
- package/skills/slidev/references/core-layouts.md +286 -0
- package/skills/slidev/references/core-syntax.md +155 -0
- package/skills/slidev/references/diagram-latex.md +55 -0
- package/skills/slidev/references/diagram-mermaid.md +44 -0
- package/skills/slidev/references/diagram-plantuml.md +45 -0
- package/skills/slidev/references/editor-monaco-run.md +44 -0
- package/skills/slidev/references/editor-monaco-write.md +24 -0
- package/skills/slidev/references/editor-monaco.md +50 -0
- package/skills/slidev/references/editor-prettier.md +40 -0
- package/skills/slidev/references/editor-side.md +23 -0
- package/skills/slidev/references/editor-vscode.md +55 -0
- package/skills/slidev/references/layout-canvas-size.md +25 -0
- package/skills/slidev/references/layout-draggable.md +57 -0
- package/skills/slidev/references/layout-global-layers.md +50 -0
- package/skills/slidev/references/layout-slots.md +75 -0
- package/skills/slidev/references/layout-transform.md +33 -0
- package/skills/slidev/references/layout-zoom.md +39 -0
- package/skills/slidev/references/presenter-notes-ruby.md +35 -0
- package/skills/slidev/references/presenter-recording.md +30 -0
- package/skills/slidev/references/presenter-remote.md +40 -0
- package/skills/slidev/references/presenter-timer.md +34 -0
- package/skills/slidev/references/style-direction.md +34 -0
- package/skills/slidev/references/style-icons.md +46 -0
- package/skills/slidev/references/style-scoped.md +50 -0
- package/skills/slidev/references/syntax-block-frontmatter.md +39 -0
- package/skills/slidev/references/syntax-frontmatter-merging.md +49 -0
- package/skills/slidev/references/syntax-importing-slides.md +60 -0
- package/skills/slidev/references/syntax-mdc.md +51 -0
- package/skills/slidev/references/tool-eject-theme.md +27 -0
- package/skills/tsdown/LICENSE.md +22 -0
- package/skills/tsdown/README.md +77 -0
- package/skills/tsdown/SKILL.md +407 -0
- package/skills/tsdown/SYNC.md +5 -0
- package/skills/tsdown/references/README.md +139 -0
- package/skills/tsdown/references/advanced-benchmark.md +8 -0
- package/skills/tsdown/references/advanced-ci.md +89 -0
- package/skills/tsdown/references/advanced-hooks.md +363 -0
- package/skills/tsdown/references/advanced-plugins.md +381 -0
- package/skills/tsdown/references/advanced-programmatic.md +378 -0
- package/skills/tsdown/references/advanced-rolldown-options.md +117 -0
- package/skills/tsdown/references/guide-getting-started.md +178 -0
- package/skills/tsdown/references/guide-introduction.md +42 -0
- package/skills/tsdown/references/guide-migrate-from-tsup.md +189 -0
- package/skills/tsdown/references/option-cjs-default.md +98 -0
- package/skills/tsdown/references/option-cleaning.md +275 -0
- package/skills/tsdown/references/option-config-file.md +281 -0
- package/skills/tsdown/references/option-css.md +301 -0
- package/skills/tsdown/references/option-dependencies.md +385 -0
- package/skills/tsdown/references/option-dts.md +251 -0
- package/skills/tsdown/references/option-entry.md +211 -0
- package/skills/tsdown/references/option-exe.md +120 -0
- package/skills/tsdown/references/option-lint.md +127 -0
- package/skills/tsdown/references/option-log-level.md +91 -0
- package/skills/tsdown/references/option-minification.md +177 -0
- package/skills/tsdown/references/option-output-directory.md +270 -0
- package/skills/tsdown/references/option-output-format.md +181 -0
- package/skills/tsdown/references/option-package-exports.md +320 -0
- package/skills/tsdown/references/option-platform.md +256 -0
- package/skills/tsdown/references/option-root.md +88 -0
- package/skills/tsdown/references/option-shims.md +299 -0
- package/skills/tsdown/references/option-sourcemap.md +301 -0
- package/skills/tsdown/references/option-target.md +222 -0
- package/skills/tsdown/references/option-tree-shaking.md +335 -0
- package/skills/tsdown/references/option-unbundle.md +310 -0
- package/skills/tsdown/references/option-watch-mode.md +261 -0
- package/skills/tsdown/references/recipe-react.md +338 -0
- package/skills/tsdown/references/recipe-solid.md +46 -0
- package/skills/tsdown/references/recipe-svelte.md +54 -0
- package/skills/tsdown/references/recipe-vue.md +387 -0
- package/skills/tsdown/references/recipe-wasm.md +125 -0
- package/skills/tsdown/references/reference-cli.md +472 -0
- package/skills/turborepo/LICENSE.md +7 -0
- package/skills/turborepo/SKILL.md +951 -0
- package/skills/turborepo/SYNC.md +5 -0
- package/skills/turborepo/command/turborepo.md +70 -0
- package/skills/turborepo/references/best-practices/RULE.md +241 -0
- package/skills/turborepo/references/best-practices/dependencies.md +246 -0
- package/skills/turborepo/references/best-practices/packages.md +335 -0
- package/skills/turborepo/references/best-practices/structure.md +297 -0
- package/skills/turborepo/references/boundaries/RULE.md +126 -0
- package/skills/turborepo/references/caching/RULE.md +153 -0
- package/skills/turborepo/references/caching/gotchas.md +190 -0
- package/skills/turborepo/references/caching/remote-cache.md +127 -0
- package/skills/turborepo/references/ci/RULE.md +79 -0
- package/skills/turborepo/references/ci/github-actions.md +162 -0
- package/skills/turborepo/references/ci/patterns.md +145 -0
- package/skills/turborepo/references/ci/vercel.md +103 -0
- package/skills/turborepo/references/cli/RULE.md +100 -0
- package/skills/turborepo/references/cli/commands.md +297 -0
- package/skills/turborepo/references/configuration/RULE.md +235 -0
- package/skills/turborepo/references/configuration/global-options.md +239 -0
- package/skills/turborepo/references/configuration/gotchas.md +368 -0
- package/skills/turborepo/references/configuration/tasks.md +325 -0
- package/skills/turborepo/references/environment/RULE.md +123 -0
- package/skills/turborepo/references/environment/gotchas.md +175 -0
- package/skills/turborepo/references/environment/modes.md +101 -0
- package/skills/turborepo/references/filtering/RULE.md +148 -0
- package/skills/turborepo/references/filtering/patterns.md +152 -0
- package/skills/turborepo/references/watch/RULE.md +99 -0
- package/skills/vercel-cli-with-tokens/SKILL.md +328 -0
- package/skills/vercel-cli-with-tokens/SYNC.md +5 -0
- package/skills/vue/SKILL.md +90 -0
- package/skills/vue/references/composables.md +54 -0
- package/skills/vue/references/lifecycle.md +31 -0
- package/skills/vue/references/props-emits.md +62 -0
- package/skills/vue/references/provide-inject.md +47 -0
- package/skills/vue/references/state.md +65 -0
- package/skills/vue-antdv-tailwind/SKILL.md +33 -0
- package/skills/vue-antdv-tailwind/references/styling-rules.md +61 -0
- package/skills/vue-best-practices/LICENSE.md +21 -0
- package/skills/vue-best-practices/SKILL.md +154 -0
- package/skills/vue-best-practices/SYNC.md +5 -0
- package/skills/vue-best-practices/references/animation-class-based-technique.md +254 -0
- package/skills/vue-best-practices/references/animation-state-driven-technique.md +291 -0
- package/skills/vue-best-practices/references/component-async.md +97 -0
- package/skills/vue-best-practices/references/component-data-flow.md +307 -0
- package/skills/vue-best-practices/references/component-fallthrough-attrs.md +174 -0
- package/skills/vue-best-practices/references/component-keep-alive.md +137 -0
- package/skills/vue-best-practices/references/component-slots.md +216 -0
- package/skills/vue-best-practices/references/component-suspense.md +228 -0
- package/skills/vue-best-practices/references/component-teleport.md +108 -0
- package/skills/vue-best-practices/references/component-transition-group.md +128 -0
- package/skills/vue-best-practices/references/component-transition.md +125 -0
- package/skills/vue-best-practices/references/composables.md +290 -0
- package/skills/vue-best-practices/references/directives.md +162 -0
- package/skills/vue-best-practices/references/perf-avoid-component-abstraction-in-lists.md +159 -0
- package/skills/vue-best-practices/references/perf-v-once-v-memo-directives.md +182 -0
- package/skills/vue-best-practices/references/perf-virtualize-large-lists.md +187 -0
- package/skills/vue-best-practices/references/plugins.md +166 -0
- package/skills/vue-best-practices/references/reactivity.md +344 -0
- package/skills/vue-best-practices/references/render-functions.md +201 -0
- package/skills/vue-best-practices/references/sfc.md +310 -0
- package/skills/vue-best-practices/references/state-management.md +135 -0
- package/skills/vue-best-practices/references/updated-hook-performance.md +187 -0
- package/skills/vue-jsx-best-practices/LICENSE.md +21 -0
- package/skills/vue-jsx-best-practices/SKILL.md +12 -0
- package/skills/vue-jsx-best-practices/SYNC.md +5 -0
- package/skills/vue-jsx-best-practices/reference/render-function-jsx-vue-vs-react.md +141 -0
- package/skills/vue-options/SKILL.md +98 -0
- package/skills/vue-options/references/lifecycle.md +107 -0
- package/skills/vue-options/references/mixins.md +96 -0
- package/skills/vue-options/references/props-emits.md +106 -0
- package/skills/vue-options/references/provide-inject.md +92 -0
- package/skills/vue-options/references/state.md +115 -0
- package/skills/vue-options-api-best-practices/LICENSE.md +21 -0
- package/skills/vue-options-api-best-practices/SKILL.md +23 -0
- package/skills/vue-options-api-best-practices/SYNC.md +5 -0
- package/skills/vue-options-api-best-practices/reference/no-arrow-functions-in-lifecycle-hooks.md +95 -0
- package/skills/vue-options-api-best-practices/reference/no-arrow-functions-in-methods.md +68 -0
- package/skills/vue-options-api-best-practices/reference/stateful-methods-lifecycle.md +61 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-arrow-functions-validators.md +141 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-computed-return-types.md +192 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-proptype-complex-types.md +212 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-provide-inject-limitations.md +135 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-type-event-handlers.md +202 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-use-definecomponent.md +172 -0
- package/skills/vue-options-api-best-practices/reference/ts-strict-mode-options-api.md +197 -0
- package/skills/vue-pinia-best-practices/LICENSE.md +21 -0
- package/skills/vue-pinia-best-practices/SKILL.md +21 -0
- package/skills/vue-pinia-best-practices/SYNC.md +5 -0
- package/skills/vue-pinia-best-practices/reference/pinia-no-active-pinia-error.md +248 -0
- package/skills/vue-pinia-best-practices/reference/pinia-setup-store-return-all-state.md +227 -0
- package/skills/vue-pinia-best-practices/reference/pinia-store-destructuring-breaks-reactivity.md +193 -0
- package/skills/vue-pinia-best-practices/reference/state-url-for-ephemeral-filters.md +238 -0
- package/skills/vue-pinia-best-practices/reference/state-use-pinia-for-large-apps.md +262 -0
- package/skills/vue-pinia-best-practices/reference/store-method-binding-parentheses.md +191 -0
- package/skills/vue-router-best-practices/LICENSE.md +21 -0
- package/skills/vue-router-best-practices/SKILL.md +23 -0
- package/skills/vue-router-best-practices/SYNC.md +5 -0
- package/skills/vue-router-best-practices/reference/router-beforeenter-no-param-trigger.md +167 -0
- package/skills/vue-router-best-practices/reference/router-beforerouteenter-no-this.md +176 -0
- package/skills/vue-router-best-practices/reference/router-guard-async-await-pattern.md +227 -0
- package/skills/vue-router-best-practices/reference/router-navigation-guard-infinite-loop.md +187 -0
- package/skills/vue-router-best-practices/reference/router-navigation-guard-next-deprecated.md +150 -0
- package/skills/vue-router-best-practices/reference/router-param-change-no-lifecycle.md +181 -0
- package/skills/vue-router-best-practices/reference/router-simple-routing-cleanup.md +209 -0
- package/skills/vue-router-best-practices/reference/router-use-vue-router-for-production.md +183 -0
- package/skills/vue-testing-best-practices/LICENSE.md +21 -0
- package/skills/vue-testing-best-practices/SKILL.md +29 -0
- package/skills/vue-testing-best-practices/SYNC.md +5 -0
- package/skills/vue-testing-best-practices/reference/async-component-testing.md +163 -0
- package/skills/vue-testing-best-practices/reference/teleport-testing-complexity.md +158 -0
- package/skills/vue-testing-best-practices/reference/testing-async-await-flushpromises.md +175 -0
- package/skills/vue-testing-best-practices/reference/testing-browser-vs-node-runners.md +208 -0
- package/skills/vue-testing-best-practices/reference/testing-component-blackbox-approach.md +144 -0
- package/skills/vue-testing-best-practices/reference/testing-composables-helper-wrapper.md +238 -0
- package/skills/vue-testing-best-practices/reference/testing-e2e-playwright-recommended.md +242 -0
- package/skills/vue-testing-best-practices/reference/testing-no-snapshot-only.md +197 -0
- package/skills/vue-testing-best-practices/reference/testing-pinia-store-setup.md +228 -0
- package/skills/vue-testing-best-practices/reference/testing-suspense-async-components.md +229 -0
- package/skills/vue-testing-best-practices/reference/testing-vitest-recommended-for-vue.md +204 -0
- package/skills/vueuse-functions/LICENSE.md +21 -0
- package/skills/vueuse-functions/SKILL.md +419 -0
- package/skills/vueuse-functions/SYNC.md +5 -0
- package/skills/vueuse-functions/references/computedAsync.md +195 -0
- package/skills/vueuse-functions/references/computedEager.md +62 -0
- package/skills/vueuse-functions/references/computedInject.md +137 -0
- package/skills/vueuse-functions/references/computedWithControl.md +98 -0
- package/skills/vueuse-functions/references/createEventHook.md +86 -0
- package/skills/vueuse-functions/references/createGenericProjection.md +25 -0
- package/skills/vueuse-functions/references/createGlobalState.md +95 -0
- package/skills/vueuse-functions/references/createInjectionState.md +215 -0
- package/skills/vueuse-functions/references/createProjection.md +31 -0
- package/skills/vueuse-functions/references/createRef.md +54 -0
- package/skills/vueuse-functions/references/createReusableTemplate.md +357 -0
- package/skills/vueuse-functions/references/createSharedComposable.md +42 -0
- package/skills/vueuse-functions/references/createTemplatePromise.md +306 -0
- package/skills/vueuse-functions/references/createUnrefFn.md +51 -0
- package/skills/vueuse-functions/references/extendRef.md +76 -0
- package/skills/vueuse-functions/references/from.md +80 -0
- package/skills/vueuse-functions/references/get.md +30 -0
- package/skills/vueuse-functions/references/injectLocal.md +35 -0
- package/skills/vueuse-functions/references/isDefined.md +31 -0
- package/skills/vueuse-functions/references/logicAnd.md +40 -0
- package/skills/vueuse-functions/references/logicNot.md +36 -0
- package/skills/vueuse-functions/references/logicOr.md +40 -0
- package/skills/vueuse-functions/references/makeDestructurable.md +41 -0
- package/skills/vueuse-functions/references/onClickOutside.md +221 -0
- package/skills/vueuse-functions/references/onElementRemoval.md +88 -0
- package/skills/vueuse-functions/references/onKeyStroke.md +211 -0
- package/skills/vueuse-functions/references/onLongPress.md +227 -0
- package/skills/vueuse-functions/references/onStartTyping.md +53 -0
- package/skills/vueuse-functions/references/provideLocal.md +37 -0
- package/skills/vueuse-functions/references/reactify.md +144 -0
- package/skills/vueuse-functions/references/reactifyObject.md +62 -0
- package/skills/vueuse-functions/references/reactiveComputed.md +34 -0
- package/skills/vueuse-functions/references/reactiveOmit.md +86 -0
- package/skills/vueuse-functions/references/reactivePick.md +106 -0
- package/skills/vueuse-functions/references/refAutoReset.md +46 -0
- package/skills/vueuse-functions/references/refDebounced.md +81 -0
- package/skills/vueuse-functions/references/refDefault.md +36 -0
- package/skills/vueuse-functions/references/refManualReset.md +44 -0
- package/skills/vueuse-functions/references/refThrottled.md +99 -0
- package/skills/vueuse-functions/references/refWithControl.md +146 -0
- package/skills/vueuse-functions/references/set.md +30 -0
- package/skills/vueuse-functions/references/syncRef.md +195 -0
- package/skills/vueuse-functions/references/syncRefs.md +128 -0
- package/skills/vueuse-functions/references/templateRef.md +86 -0
- package/skills/vueuse-functions/references/toObserver.md +38 -0
- package/skills/vueuse-functions/references/toReactive.md +41 -0
- package/skills/vueuse-functions/references/toRef.md +75 -0
- package/skills/vueuse-functions/references/toRefs.md +81 -0
- package/skills/vueuse-functions/references/tryOnBeforeMount.md +34 -0
- package/skills/vueuse-functions/references/tryOnBeforeUnmount.md +32 -0
- package/skills/vueuse-functions/references/tryOnMounted.md +34 -0
- package/skills/vueuse-functions/references/tryOnScopeDispose.md +31 -0
- package/skills/vueuse-functions/references/tryOnUnmounted.md +32 -0
- package/skills/vueuse-functions/references/unrefElement.md +54 -0
- package/skills/vueuse-functions/references/until.md +161 -0
- package/skills/vueuse-functions/references/useAbs.md +31 -0
- package/skills/vueuse-functions/references/useActiveElement.md +85 -0
- package/skills/vueuse-functions/references/useAnimate.md +181 -0
- package/skills/vueuse-functions/references/useArrayDifference.md +84 -0
- package/skills/vueuse-functions/references/useArrayEvery.md +59 -0
- package/skills/vueuse-functions/references/useArrayFilter.md +63 -0
- package/skills/vueuse-functions/references/useArrayFind.md +50 -0
- package/skills/vueuse-functions/references/useArrayFindIndex.md +59 -0
- package/skills/vueuse-functions/references/useArrayFindLast.md +52 -0
- package/skills/vueuse-functions/references/useArrayIncludes.md +63 -0
- package/skills/vueuse-functions/references/useArrayJoin.md +74 -0
- package/skills/vueuse-functions/references/useArrayMap.md +59 -0
- package/skills/vueuse-functions/references/useArrayReduce.md +81 -0
- package/skills/vueuse-functions/references/useArraySome.md +59 -0
- package/skills/vueuse-functions/references/useArrayUnique.md +76 -0
- package/skills/vueuse-functions/references/useAsyncQueue.md +136 -0
- package/skills/vueuse-functions/references/useAsyncState.md +185 -0
- package/skills/vueuse-functions/references/useAsyncValidator.md +70 -0
- package/skills/vueuse-functions/references/useAuth.md +123 -0
- package/skills/vueuse-functions/references/useAverage.md +36 -0
- package/skills/vueuse-functions/references/useAxios.md +325 -0
- package/skills/vueuse-functions/references/useBase64.md +136 -0
- package/skills/vueuse-functions/references/useBattery.md +78 -0
- package/skills/vueuse-functions/references/useBluetooth.md +175 -0
- package/skills/vueuse-functions/references/useBreakpoints.md +172 -0
- package/skills/vueuse-functions/references/useBroadcastChannel.md +74 -0
- package/skills/vueuse-functions/references/useBrowserLocation.md +83 -0
- package/skills/vueuse-functions/references/useCached.md +52 -0
- package/skills/vueuse-functions/references/useCeil.md +31 -0
- package/skills/vueuse-functions/references/useChangeCase.md +79 -0
- package/skills/vueuse-functions/references/useClamp.md +85 -0
- package/skills/vueuse-functions/references/useClipboard.md +123 -0
- package/skills/vueuse-functions/references/useClipboardItems.md +94 -0
- package/skills/vueuse-functions/references/useCloned.md +91 -0
- package/skills/vueuse-functions/references/useColorMode.md +172 -0
- package/skills/vueuse-functions/references/useConfirmDialog.md +159 -0
- package/skills/vueuse-functions/references/useCookies.md +162 -0
- package/skills/vueuse-functions/references/useCountdown.md +105 -0
- package/skills/vueuse-functions/references/useCounter.md +86 -0
- package/skills/vueuse-functions/references/useCssSupports.md +35 -0
- package/skills/vueuse-functions/references/useCssVar.md +50 -0
- package/skills/vueuse-functions/references/useCurrentElement.md +61 -0
- package/skills/vueuse-functions/references/useCycleList.md +75 -0
- package/skills/vueuse-functions/references/useDark.md +144 -0
- package/skills/vueuse-functions/references/useDateFormat.md +145 -0
- package/skills/vueuse-functions/references/useDebounceFn.md +100 -0
- package/skills/vueuse-functions/references/useDebouncedRefHistory.md +40 -0
- package/skills/vueuse-functions/references/useDeviceMotion.md +86 -0
- package/skills/vueuse-functions/references/useDeviceOrientation.md +62 -0
- package/skills/vueuse-functions/references/useDevicePixelRatio.md +44 -0
- package/skills/vueuse-functions/references/useDevicesList.md +90 -0
- package/skills/vueuse-functions/references/useDisplayMedia.md +71 -0
- package/skills/vueuse-functions/references/useDocumentVisibility.md +45 -0
- package/skills/vueuse-functions/references/useDraggable.md +317 -0
- package/skills/vueuse-functions/references/useDrauu.md +65 -0
- package/skills/vueuse-functions/references/useDropZone.md +83 -0
- package/skills/vueuse-functions/references/useElementBounding.md +131 -0
- package/skills/vueuse-functions/references/useElementByPoint.md +48 -0
- package/skills/vueuse-functions/references/useElementHover.md +79 -0
- package/skills/vueuse-functions/references/useElementSize.md +78 -0
- package/skills/vueuse-functions/references/useElementVisibility.md +123 -0
- package/skills/vueuse-functions/references/useEventBus.md +101 -0
- package/skills/vueuse-functions/references/useEventListener.md +226 -0
- package/skills/vueuse-functions/references/useEventSource.md +204 -0
- package/skills/vueuse-functions/references/useExtractedObservable.md +198 -0
- package/skills/vueuse-functions/references/useEyeDropper.md +71 -0
- package/skills/vueuse-functions/references/useFavicon.md +67 -0
- package/skills/vueuse-functions/references/useFetch.md +546 -0
- package/skills/vueuse-functions/references/useFileDialog.md +91 -0
- package/skills/vueuse-functions/references/useFileSystemAccess.md +162 -0
- package/skills/vueuse-functions/references/useFirestore.md +129 -0
- package/skills/vueuse-functions/references/useFloor.md +31 -0
- package/skills/vueuse-functions/references/useFocus.md +99 -0
- package/skills/vueuse-functions/references/useFocusTrap.md +245 -0
- package/skills/vueuse-functions/references/useFocusWithin.md +57 -0
- package/skills/vueuse-functions/references/useFps.md +28 -0
- package/skills/vueuse-functions/references/useFullscreen.md +75 -0
- package/skills/vueuse-functions/references/useFuse.md +107 -0
- package/skills/vueuse-functions/references/useGamepad.md +222 -0
- package/skills/vueuse-functions/references/useGeolocation.md +85 -0
- package/skills/vueuse-functions/references/useIDBKeyval.md +93 -0
- package/skills/vueuse-functions/references/useIdle.md +88 -0
- package/skills/vueuse-functions/references/useImage.md +86 -0
- package/skills/vueuse-functions/references/useInfiniteScroll.md +157 -0
- package/skills/vueuse-functions/references/useIntersectionObserver.md +118 -0
- package/skills/vueuse-functions/references/useInterval.md +112 -0
- package/skills/vueuse-functions/references/useIntervalFn.md +50 -0
- package/skills/vueuse-functions/references/useIpcRenderer.md +144 -0
- package/skills/vueuse-functions/references/useIpcRendererInvoke.md +58 -0
- package/skills/vueuse-functions/references/useIpcRendererOn.md +52 -0
- package/skills/vueuse-functions/references/useJwt.md +57 -0
- package/skills/vueuse-functions/references/useKeyModifier.md +87 -0
- package/skills/vueuse-functions/references/useLastChanged.md +63 -0
- package/skills/vueuse-functions/references/useLocalStorage.md +41 -0
- package/skills/vueuse-functions/references/useMagicKeys.md +245 -0
- package/skills/vueuse-functions/references/useManualRefHistory.md +204 -0
- package/skills/vueuse-functions/references/useMath.md +47 -0
- package/skills/vueuse-functions/references/useMax.md +36 -0
- package/skills/vueuse-functions/references/useMediaControls.md +571 -0
- package/skills/vueuse-functions/references/useMediaQuery.md +53 -0
- package/skills/vueuse-functions/references/useMemoize.md +175 -0
- package/skills/vueuse-functions/references/useMemory.md +71 -0
- package/skills/vueuse-functions/references/useMin.md +36 -0
- package/skills/vueuse-functions/references/useMounted.md +38 -0
- package/skills/vueuse-functions/references/useMouse.md +113 -0
- package/skills/vueuse-functions/references/useMouseInElement.md +123 -0
- package/skills/vueuse-functions/references/useMousePressed.md +112 -0
- package/skills/vueuse-functions/references/useMutationObserver.md +61 -0
- package/skills/vueuse-functions/references/useNProgress.md +78 -0
- package/skills/vueuse-functions/references/useNavigatorLanguage.md +57 -0
- package/skills/vueuse-functions/references/useNetwork.md +106 -0
- package/skills/vueuse-functions/references/useNow.md +79 -0
- package/skills/vueuse-functions/references/useObjectUrl.md +55 -0
- package/skills/vueuse-functions/references/useObservable.md +91 -0
- package/skills/vueuse-functions/references/useOffsetPagination.md +199 -0
- package/skills/vueuse-functions/references/useOnline.md +41 -0
- package/skills/vueuse-functions/references/usePageLeave.md +42 -0
- package/skills/vueuse-functions/references/useParallax.md +58 -0
- package/skills/vueuse-functions/references/useParentElement.md +54 -0
- package/skills/vueuse-functions/references/usePerformanceObserver.md +48 -0
- package/skills/vueuse-functions/references/usePermission.md +79 -0
- package/skills/vueuse-functions/references/usePointer.md +89 -0
- package/skills/vueuse-functions/references/usePointerLock.md +60 -0
- package/skills/vueuse-functions/references/usePointerSwipe.md +80 -0
- package/skills/vueuse-functions/references/usePrecision.md +49 -0
- package/skills/vueuse-functions/references/usePreferredColorScheme.md +42 -0
- package/skills/vueuse-functions/references/usePreferredContrast.md +42 -0
- package/skills/vueuse-functions/references/usePreferredDark.md +41 -0
- package/skills/vueuse-functions/references/usePreferredLanguages.md +41 -0
- package/skills/vueuse-functions/references/usePreferredReducedMotion.md +42 -0
- package/skills/vueuse-functions/references/usePreferredReducedTransparency.md +42 -0
- package/skills/vueuse-functions/references/usePrevious.md +40 -0
- package/skills/vueuse-functions/references/useProjection.md +38 -0
- package/skills/vueuse-functions/references/useQRCode.md +53 -0
- package/skills/vueuse-functions/references/useRTDB.md +83 -0
- package/skills/vueuse-functions/references/useRafFn.md +68 -0
- package/skills/vueuse-functions/references/useRefHistory.md +285 -0
- package/skills/vueuse-functions/references/useResizeObserver.md +109 -0
- package/skills/vueuse-functions/references/useRound.md +31 -0
- package/skills/vueuse-functions/references/useRouteHash.md +27 -0
- package/skills/vueuse-functions/references/useRouteParams.md +38 -0
- package/skills/vueuse-functions/references/useRouteQuery.md +79 -0
- package/skills/vueuse-functions/references/useSSRWidth.md +47 -0
- package/skills/vueuse-functions/references/useScreenOrientation.md +96 -0
- package/skills/vueuse-functions/references/useScreenSafeArea.md +60 -0
- package/skills/vueuse-functions/references/useScriptTag.md +116 -0
- package/skills/vueuse-functions/references/useScroll.md +238 -0
- package/skills/vueuse-functions/references/useScrollLock.md +66 -0
- package/skills/vueuse-functions/references/useSessionStorage.md +41 -0
- package/skills/vueuse-functions/references/useShare.md +68 -0
- package/skills/vueuse-functions/references/useSortable.md +235 -0
- package/skills/vueuse-functions/references/useSorted.md +90 -0
- package/skills/vueuse-functions/references/useSpeechRecognition.md +94 -0
- package/skills/vueuse-functions/references/useSpeechSynthesis.md +105 -0
- package/skills/vueuse-functions/references/useStepper.md +137 -0
- package/skills/vueuse-functions/references/useStorage.md +278 -0
- package/skills/vueuse-functions/references/useStorageAsync.md +136 -0
- package/skills/vueuse-functions/references/useStyleTag.md +131 -0
- package/skills/vueuse-functions/references/useSubject.md +77 -0
- package/skills/vueuse-functions/references/useSubscription.md +33 -0
- package/skills/vueuse-functions/references/useSum.md +36 -0
- package/skills/vueuse-functions/references/useSupported.md +29 -0
- package/skills/vueuse-functions/references/useSwipe.md +75 -0
- package/skills/vueuse-functions/references/useTemplateRefsList.md +37 -0
- package/skills/vueuse-functions/references/useTextDirection.md +75 -0
- package/skills/vueuse-functions/references/useTextSelection.md +40 -0
- package/skills/vueuse-functions/references/useTextareaAutosize.md +97 -0
- package/skills/vueuse-functions/references/useThrottleFn.md +57 -0
- package/skills/vueuse-functions/references/useThrottledRefHistory.md +47 -0
- package/skills/vueuse-functions/references/useTimeAgo.md +154 -0
- package/skills/vueuse-functions/references/useTimeAgoIntl.md +117 -0
- package/skills/vueuse-functions/references/useTimeout.md +113 -0
- package/skills/vueuse-functions/references/useTimeoutFn.md +51 -0
- package/skills/vueuse-functions/references/useTimeoutPoll.md +47 -0
- package/skills/vueuse-functions/references/useTimestamp.md +89 -0
- package/skills/vueuse-functions/references/useTitle.md +113 -0
- package/skills/vueuse-functions/references/useToNumber.md +54 -0
- package/skills/vueuse-functions/references/useToString.md +34 -0
- package/skills/vueuse-functions/references/useToggle.md +103 -0
- package/skills/vueuse-functions/references/useTransition.md +265 -0
- package/skills/vueuse-functions/references/useTrunc.md +33 -0
- package/skills/vueuse-functions/references/useUrlSearchParams.md +121 -0
- package/skills/vueuse-functions/references/useUserMedia.md +1138 -0
- package/skills/vueuse-functions/references/useVModel.md +182 -0
- package/skills/vueuse-functions/references/useVModels.md +67 -0
- package/skills/vueuse-functions/references/useVibrate.md +87 -0
- package/skills/vueuse-functions/references/useVirtualList.md +182 -0
- package/skills/vueuse-functions/references/useWakeLock.md +50 -0
- package/skills/vueuse-functions/references/useWebNotification.md +176 -0
- package/skills/vueuse-functions/references/useWebSocket.md +299 -0
- package/skills/vueuse-functions/references/useWebWorker.md +60 -0
- package/skills/vueuse-functions/references/useWebWorkerFn.md +102 -0
- package/skills/vueuse-functions/references/useWindowFocus.md +46 -0
- package/skills/vueuse-functions/references/useWindowScroll.md +61 -0
- package/skills/vueuse-functions/references/useWindowSize.md +76 -0
- package/skills/vueuse-functions/references/useZoomFactor.md +53 -0
- package/skills/vueuse-functions/references/useZoomLevel.md +53 -0
- package/skills/vueuse-functions/references/watchArray.md +53 -0
- package/skills/vueuse-functions/references/watchAtMost.md +55 -0
- package/skills/vueuse-functions/references/watchDebounced.md +101 -0
- package/skills/vueuse-functions/references/watchDeep.md +54 -0
- package/skills/vueuse-functions/references/watchExtractedObservable.md +192 -0
- package/skills/vueuse-functions/references/watchIgnorable.md +120 -0
- package/skills/vueuse-functions/references/watchImmediate.md +44 -0
- package/skills/vueuse-functions/references/watchOnce.md +41 -0
- package/skills/vueuse-functions/references/watchPausable.md +86 -0
- package/skills/vueuse-functions/references/watchThrottled.md +108 -0
- package/skills/vueuse-functions/references/watchTriggerable.md +98 -0
- package/skills/vueuse-functions/references/watchWithFilter.md +54 -0
- package/skills/vueuse-functions/references/whenever.md +100 -0
- package/skills/web-design-guidelines/SKILL.md +39 -0
- package/skills/web-design-guidelines/SYNC.md +5 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Compound Components Over Polymorphic Children
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: flexible composition, clearer API
|
|
5
|
+
tags: design-system, components, composition
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Compound Components Over Polymorphic Children
|
|
9
|
+
|
|
10
|
+
Don't create components that can accept a string if they aren't a text node. If
|
|
11
|
+
a component can receive a string child, it must be a dedicated `*Text`
|
|
12
|
+
component. For components like buttons, which can have both a View (or
|
|
13
|
+
Pressable) together with text, use compound components, such a `Button`,
|
|
14
|
+
`ButtonText`, and `ButtonIcon`.
|
|
15
|
+
|
|
16
|
+
**Incorrect (polymorphic children):**
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
import { Pressable, Text } from 'react-native'
|
|
20
|
+
|
|
21
|
+
type ButtonProps = {
|
|
22
|
+
children: string | React.ReactNode
|
|
23
|
+
icon?: React.ReactNode
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function Button({ children, icon }: ButtonProps) {
|
|
27
|
+
return (
|
|
28
|
+
<Pressable>
|
|
29
|
+
{icon}
|
|
30
|
+
{typeof children === 'string' ? <Text>{children}</Text> : children}
|
|
31
|
+
</Pressable>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Usage is ambiguous
|
|
36
|
+
<Button icon={<Icon />}>Save</Button>
|
|
37
|
+
<Button><CustomText>Save</CustomText></Button>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Correct (compound components):**
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
import { Pressable, Text } from 'react-native'
|
|
44
|
+
|
|
45
|
+
function Button({ children }: { children: React.ReactNode }) {
|
|
46
|
+
return <Pressable>{children}</Pressable>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function ButtonText({ children }: { children: React.ReactNode }) {
|
|
50
|
+
return <Text>{children}</Text>
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function ButtonIcon({ children }: { children: React.ReactNode }) {
|
|
54
|
+
return <>{children}</>
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Usage is explicit and composable
|
|
58
|
+
<Button>
|
|
59
|
+
<ButtonIcon><SaveIcon /></ButtonIcon>
|
|
60
|
+
<ButtonText>Save</ButtonText>
|
|
61
|
+
</Button>
|
|
62
|
+
|
|
63
|
+
<Button>
|
|
64
|
+
<ButtonText>Cancel</ButtonText>
|
|
65
|
+
</Button>
|
|
66
|
+
```
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Load fonts natively at build time
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: fonts available at launch, no async loading
|
|
5
|
+
tags: fonts, expo, performance, config-plugin
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Expo Config Plugin for Font Loading
|
|
9
|
+
|
|
10
|
+
Use the `expo-font` config plugin to embed fonts at build time instead of
|
|
11
|
+
`useFonts` or `Font.loadAsync`. Embedded fonts are more efficient.
|
|
12
|
+
|
|
13
|
+
**Incorrect (async font loading):**
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { useFonts } from 'expo-font'
|
|
17
|
+
import { Text, View } from 'react-native'
|
|
18
|
+
|
|
19
|
+
function App() {
|
|
20
|
+
const [fontsLoaded] = useFonts({
|
|
21
|
+
'Geist-Bold': require('./assets/fonts/Geist-Bold.otf'),
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
if (!fontsLoaded) {
|
|
25
|
+
return null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<View>
|
|
30
|
+
<Text style={{ fontFamily: 'Geist-Bold' }}>Hello</Text>
|
|
31
|
+
</View>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Correct (config plugin, fonts embedded at build):**
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
// app.json
|
|
40
|
+
{
|
|
41
|
+
"expo": {
|
|
42
|
+
"plugins": [
|
|
43
|
+
[
|
|
44
|
+
"expo-font",
|
|
45
|
+
{
|
|
46
|
+
"fonts": ["./assets/fonts/Geist-Bold.otf"]
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
import { Text, View } from 'react-native'
|
|
56
|
+
|
|
57
|
+
function App() {
|
|
58
|
+
// No loading state needed—font is already available
|
|
59
|
+
return (
|
|
60
|
+
<View>
|
|
61
|
+
<Text style={{ fontFamily: 'Geist-Bold' }}>Hello</Text>
|
|
62
|
+
</View>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
After adding fonts to the config plugin, run `npx expo prebuild` and rebuild the
|
|
68
|
+
native app.
|
|
69
|
+
|
|
70
|
+
Reference:
|
|
71
|
+
[Expo Font Documentation](https://docs.expo.dev/versions/latest/sdk/font/)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Import from Design System Folder
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: enables global changes and easy refactoring
|
|
5
|
+
tags: imports, architecture, design-system
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Import from Design System Folder
|
|
9
|
+
|
|
10
|
+
Re-export dependencies from a design system folder. App code imports from there,
|
|
11
|
+
not directly from packages. This enables global changes and easy refactoring.
|
|
12
|
+
|
|
13
|
+
**Incorrect (imports directly from package):**
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { View, Text } from 'react-native'
|
|
17
|
+
import { Button } from '@ui/button'
|
|
18
|
+
|
|
19
|
+
function Profile() {
|
|
20
|
+
return (
|
|
21
|
+
<View>
|
|
22
|
+
<Text>Hello</Text>
|
|
23
|
+
<Button>Save</Button>
|
|
24
|
+
</View>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Correct (imports from design system):**
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
// components/view.tsx
|
|
33
|
+
import { View as RNView } from 'react-native'
|
|
34
|
+
|
|
35
|
+
// ideal: pick the props you will actually use to control implementation
|
|
36
|
+
export function View(
|
|
37
|
+
props: Pick<React.ComponentProps<typeof RNView>, 'style' | 'children'>
|
|
38
|
+
) {
|
|
39
|
+
return <RNView {...props} />
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
// components/text.tsx
|
|
45
|
+
export { Text } from 'react-native'
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
// components/button.tsx
|
|
50
|
+
export { Button } from '@ui/button'
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import { View } from '@/components/view'
|
|
55
|
+
import { Text } from '@/components/text'
|
|
56
|
+
import { Button } from '@/components/button'
|
|
57
|
+
|
|
58
|
+
function Profile() {
|
|
59
|
+
return (
|
|
60
|
+
<View>
|
|
61
|
+
<Text>Hello</Text>
|
|
62
|
+
<Button>Save</Button>
|
|
63
|
+
</View>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Start by simply re-exporting. Customize later without changing app code.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Hoist Intl Formatter Creation
|
|
3
|
+
impact: LOW-MEDIUM
|
|
4
|
+
impactDescription: avoids expensive object recreation
|
|
5
|
+
tags: javascript, intl, optimization, memoization
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Hoist Intl Formatter Creation
|
|
9
|
+
|
|
10
|
+
Don't create `Intl.DateTimeFormat`, `Intl.NumberFormat`, or
|
|
11
|
+
`Intl.RelativeTimeFormat` inside render or loops. These are expensive to
|
|
12
|
+
instantiate. Hoist to module scope when the locale/options are static.
|
|
13
|
+
|
|
14
|
+
**Incorrect (new formatter every render):**
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
function Price({ amount }: { amount: number }) {
|
|
18
|
+
const formatter = new Intl.NumberFormat('en-US', {
|
|
19
|
+
style: 'currency',
|
|
20
|
+
currency: 'USD',
|
|
21
|
+
})
|
|
22
|
+
return <Text>{formatter.format(amount)}</Text>
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Correct (hoisted to module scope):**
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
const currencyFormatter = new Intl.NumberFormat('en-US', {
|
|
30
|
+
style: 'currency',
|
|
31
|
+
currency: 'USD',
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
function Price({ amount }: { amount: number }) {
|
|
35
|
+
return <Text>{currencyFormatter.format(amount)}</Text>
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**For dynamic locales, memoize:**
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
const dateFormatter = useMemo(
|
|
43
|
+
() => new Intl.DateTimeFormat(locale, { dateStyle: 'medium' }),
|
|
44
|
+
[locale]
|
|
45
|
+
)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Common formatters to hoist:**
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
// Module-level formatters
|
|
52
|
+
const dateFormatter = new Intl.DateTimeFormat('en-US', { dateStyle: 'medium' })
|
|
53
|
+
const timeFormatter = new Intl.DateTimeFormat('en-US', { timeStyle: 'short' })
|
|
54
|
+
const percentFormatter = new Intl.NumberFormat('en-US', { style: 'percent' })
|
|
55
|
+
const relativeFormatter = new Intl.RelativeTimeFormat('en-US', {
|
|
56
|
+
numeric: 'auto',
|
|
57
|
+
})
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Creating `Intl` objects is significantly more expensive than `RegExp` or plain
|
|
61
|
+
objects—each instantiation parses locale data and builds internal lookup tables.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Hoist callbacks to the root of lists
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Fewer re-renders and faster lists
|
|
5
|
+
tags: tag1, tag2
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## List performance callbacks
|
|
9
|
+
|
|
10
|
+
**Impact: HIGH (Fewer re-renders and faster lists)**
|
|
11
|
+
|
|
12
|
+
When passing callback functions to list items, create a single instance of the
|
|
13
|
+
callback at the root of the list. Items should then call it with a unique
|
|
14
|
+
identifier.
|
|
15
|
+
|
|
16
|
+
**Incorrect (creates a new callback on each render):**
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
return (
|
|
20
|
+
<LegendList
|
|
21
|
+
renderItem={({ item }) => {
|
|
22
|
+
// bad: creates a new callback on each render
|
|
23
|
+
const onPress = () => handlePress(item.id)
|
|
24
|
+
return <Item key={item.id} item={item} onPress={onPress} />
|
|
25
|
+
}}
|
|
26
|
+
/>
|
|
27
|
+
)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Correct (a single function instance passed to each item):**
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
const onPress = useCallback(() => handlePress(item.id), [handlePress, item.id])
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<LegendList
|
|
37
|
+
renderItem={({ item }) => (
|
|
38
|
+
<Item key={item.id} item={item} onPress={onPress} />
|
|
39
|
+
)}
|
|
40
|
+
/>
|
|
41
|
+
)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Reference: [Link to documentation or resource](https://example.com)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Optimize List Performance with Stable Object References
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: virtualization relies on reference stability
|
|
5
|
+
tags: lists, performance, flatlist, virtualization
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Optimize List Performance with Stable Object References
|
|
9
|
+
|
|
10
|
+
Don't map or filter data before passing to virtualized lists. Virtualization
|
|
11
|
+
relies on object reference stability to know what changed—new references cause
|
|
12
|
+
full re-renders of all visible items. Attempt to prevent frequent renders at the
|
|
13
|
+
list-parent level.
|
|
14
|
+
|
|
15
|
+
Where needed, use context selectors within list items.
|
|
16
|
+
|
|
17
|
+
**Incorrect (creates new object references on every keystroke):**
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
function DomainSearch() {
|
|
21
|
+
const { keyword, setKeyword } = useKeywordZustandState()
|
|
22
|
+
const { data: tlds } = useTlds()
|
|
23
|
+
|
|
24
|
+
// Bad: creates new objects on every render, reparenting the entire list on every keystroke
|
|
25
|
+
const domains = tlds.map((tld) => ({
|
|
26
|
+
domain: `${keyword}.${tld.name}`,
|
|
27
|
+
tld: tld.name,
|
|
28
|
+
price: tld.price,
|
|
29
|
+
}))
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<>
|
|
33
|
+
<TextInput value={keyword} onChangeText={setKeyword} />
|
|
34
|
+
<LegendList
|
|
35
|
+
data={domains}
|
|
36
|
+
renderItem={({ item }) => <DomainItem item={item} keyword={keyword} />}
|
|
37
|
+
/>
|
|
38
|
+
</>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Correct (stable references, transform inside items):**
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
const renderItem = ({ item }) => <DomainItem tld={item} />
|
|
47
|
+
|
|
48
|
+
function DomainSearch() {
|
|
49
|
+
const { data: tlds } = useTlds()
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<LegendList
|
|
53
|
+
// good: as long as the data is stable, LegendList will not re-render the entire list
|
|
54
|
+
data={tlds}
|
|
55
|
+
renderItem={renderItem}
|
|
56
|
+
/>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function DomainItem({ tld }: { tld: Tld }) {
|
|
61
|
+
// good: transform within items, and don't pass the dynamic data as a prop
|
|
62
|
+
// good: use a selector function from zustand to receive a stable string back
|
|
63
|
+
const domain = useKeywordZustandState((s) => s.keyword + '.' + tld.name)
|
|
64
|
+
return <Text>{domain}</Text>
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Updating parent array reference:**
|
|
69
|
+
|
|
70
|
+
Creating a new array instance can be okay, as long as its inner object
|
|
71
|
+
references are stable. For instance, if you sort a list of objects:
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
// good: creates a new array instance without mutating the inner objects
|
|
75
|
+
// good: parent array reference is unaffected by typing and updating "keyword"
|
|
76
|
+
const sortedTlds = tlds.toSorted((a, b) => a.name.localeCompare(b.name))
|
|
77
|
+
|
|
78
|
+
return <LegendList data={sortedTlds} renderItem={renderItem} />
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Even though this creates a new array instance `sortedTlds`, the inner object
|
|
82
|
+
references are stable.
|
|
83
|
+
|
|
84
|
+
**With zustand for dynamic data (avoids parent re-renders):**
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
const useSearchStore = create<{ keyword: string }>(() => ({ keyword: '' }))
|
|
88
|
+
|
|
89
|
+
function DomainSearch() {
|
|
90
|
+
const { data: tlds } = useTlds()
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<>
|
|
94
|
+
<SearchInput />
|
|
95
|
+
<LegendList
|
|
96
|
+
data={tlds}
|
|
97
|
+
// if you aren't using React Compiler, wrap renderItem with useCallback
|
|
98
|
+
renderItem={({ item }) => <DomainItem tld={item} />}
|
|
99
|
+
/>
|
|
100
|
+
</>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function DomainItem({ tld }: { tld: Tld }) {
|
|
105
|
+
// Select only what you need—component only re-renders when keyword changes
|
|
106
|
+
const keyword = useSearchStore((s) => s.keyword)
|
|
107
|
+
const domain = `${keyword}.${tld.name}`
|
|
108
|
+
return <Text>{domain}</Text>
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Virtualization can now skip items that haven't changed when typing. Only visible
|
|
113
|
+
items (~20) re-render on keystroke, rather than the parent.
|
|
114
|
+
|
|
115
|
+
**Deriving state within list items based on parent data (avoids parent
|
|
116
|
+
re-renders):**
|
|
117
|
+
|
|
118
|
+
For components where the data is conditional based on the parent state, this
|
|
119
|
+
pattern is even more important. For example, if you are checking if an item is
|
|
120
|
+
favorited, toggling favorites only re-renders one component if the item itself
|
|
121
|
+
is in charge of accessing the state rather than the parent:
|
|
122
|
+
|
|
123
|
+
```tsx
|
|
124
|
+
function DomainItemFavoriteButton({ tld }: { tld: Tld }) {
|
|
125
|
+
const isFavorited = useFavoritesStore((s) => s.favorites.has(tld.id))
|
|
126
|
+
return <TldFavoriteButton isFavorited={isFavorited} />
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Note: if you're using the React Compiler, you can read React Context values
|
|
131
|
+
directly within list items. Although this is slightly slower than using a
|
|
132
|
+
Zustand selector in most cases, the effect may be negligible.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Compressed Images in Lists
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: faster load times, less memory
|
|
5
|
+
tags: lists, images, performance, optimization
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Compressed Images in Lists
|
|
9
|
+
|
|
10
|
+
Always load compressed, appropriately-sized images in lists. Full-resolution
|
|
11
|
+
images consume excessive memory and cause scroll jank. Request thumbnails from
|
|
12
|
+
your server or use an image CDN with resize parameters.
|
|
13
|
+
|
|
14
|
+
**Incorrect (full-resolution images):**
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
function ProductItem({ product }: { product: Product }) {
|
|
18
|
+
return (
|
|
19
|
+
<View>
|
|
20
|
+
{/* 4000x3000 image loaded for a 100x100 thumbnail */}
|
|
21
|
+
<Image
|
|
22
|
+
source={{ uri: product.imageUrl }}
|
|
23
|
+
style={{ width: 100, height: 100 }}
|
|
24
|
+
/>
|
|
25
|
+
<Text>{product.name}</Text>
|
|
26
|
+
</View>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Correct (request appropriately-sized image):**
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
function ProductItem({ product }: { product: Product }) {
|
|
35
|
+
// Request a 200x200 image (2x for retina)
|
|
36
|
+
const thumbnailUrl = `${product.imageUrl}?w=200&h=200&fit=cover`
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<View>
|
|
40
|
+
<Image
|
|
41
|
+
source={{ uri: thumbnailUrl }}
|
|
42
|
+
style={{ width: 100, height: 100 }}
|
|
43
|
+
contentFit='cover'
|
|
44
|
+
/>
|
|
45
|
+
<Text>{product.name}</Text>
|
|
46
|
+
</View>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Use an optimized image component with built-in caching and placeholder support,
|
|
52
|
+
such as `expo-image` or `SolitoImage` (which uses `expo-image` under the hood).
|
|
53
|
+
Request images at 2x the display size for retina screens.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Avoid Inline Objects in renderItem
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: prevents unnecessary re-renders of memoized list items
|
|
5
|
+
tags: lists, performance, flatlist, virtualization, memo
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Avoid Inline Objects in renderItem
|
|
9
|
+
|
|
10
|
+
Don't create new objects inside `renderItem` to pass as props. Inline objects
|
|
11
|
+
create new references on every render, breaking memoization. Pass primitive
|
|
12
|
+
values directly from `item` instead.
|
|
13
|
+
|
|
14
|
+
**Incorrect (inline object breaks memoization):**
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
function UserList({ users }: { users: User[] }) {
|
|
18
|
+
return (
|
|
19
|
+
<LegendList
|
|
20
|
+
data={users}
|
|
21
|
+
renderItem={({ item }) => (
|
|
22
|
+
<UserRow
|
|
23
|
+
// Bad: new object on every render
|
|
24
|
+
user={{ id: item.id, name: item.name, avatar: item.avatar }}
|
|
25
|
+
/>
|
|
26
|
+
)}
|
|
27
|
+
/>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Incorrect (inline style object):**
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
renderItem={({ item }) => (
|
|
36
|
+
<UserRow
|
|
37
|
+
name={item.name}
|
|
38
|
+
// Bad: new style object on every render
|
|
39
|
+
style={{ backgroundColor: item.isActive ? 'green' : 'gray' }}
|
|
40
|
+
/>
|
|
41
|
+
)}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Correct (pass item directly or primitives):**
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
function UserList({ users }: { users: User[] }) {
|
|
48
|
+
return (
|
|
49
|
+
<LegendList
|
|
50
|
+
data={users}
|
|
51
|
+
renderItem={({ item }) => (
|
|
52
|
+
// Good: pass the item directly
|
|
53
|
+
<UserRow user={item} />
|
|
54
|
+
)}
|
|
55
|
+
/>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Correct (pass primitives, derive inside child):**
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
renderItem={({ item }) => (
|
|
64
|
+
<UserRow
|
|
65
|
+
id={item.id}
|
|
66
|
+
name={item.name}
|
|
67
|
+
isActive={item.isActive}
|
|
68
|
+
/>
|
|
69
|
+
)}
|
|
70
|
+
|
|
71
|
+
const UserRow = memo(function UserRow({ id, name, isActive }: Props) {
|
|
72
|
+
// Good: derive style inside memoized component
|
|
73
|
+
const backgroundColor = isActive ? 'green' : 'gray'
|
|
74
|
+
return <View style={[styles.row, { backgroundColor }]}>{/* ... */}</View>
|
|
75
|
+
})
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Correct (hoist static styles in module scope):**
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
const activeStyle = { backgroundColor: 'green' }
|
|
82
|
+
const inactiveStyle = { backgroundColor: 'gray' }
|
|
83
|
+
|
|
84
|
+
renderItem={({ item }) => (
|
|
85
|
+
<UserRow
|
|
86
|
+
name={item.name}
|
|
87
|
+
// Good: stable references
|
|
88
|
+
style={item.isActive ? activeStyle : inactiveStyle}
|
|
89
|
+
/>
|
|
90
|
+
)}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Passing primitives or stable references allows `memo()` to skip re-renders when
|
|
94
|
+
the actual values haven't changed.
|
|
95
|
+
|
|
96
|
+
**Note:** If you have the React Compiler enabled, it handles memoization
|
|
97
|
+
automatically and these manual optimizations become less critical.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Keep List Items Lightweight
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: reduces render time for visible items during scroll
|
|
5
|
+
tags: lists, performance, virtualization, hooks
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Keep List Items Lightweight
|
|
9
|
+
|
|
10
|
+
List items should be as inexpensive as possible to render. Minimize hooks, avoid
|
|
11
|
+
queries, and limit React Context access. Virtualized lists render many items
|
|
12
|
+
during scroll—expensive items cause jank.
|
|
13
|
+
|
|
14
|
+
**Incorrect (heavy list item):**
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
function ProductRow({ id }: { id: string }) {
|
|
18
|
+
// Bad: query inside list item
|
|
19
|
+
const { data: product } = useQuery(['product', id], () => fetchProduct(id))
|
|
20
|
+
// Bad: multiple context accesses
|
|
21
|
+
const theme = useContext(ThemeContext)
|
|
22
|
+
const user = useContext(UserContext)
|
|
23
|
+
const cart = useContext(CartContext)
|
|
24
|
+
// Bad: expensive computation
|
|
25
|
+
const recommendations = useMemo(
|
|
26
|
+
() => computeRecommendations(product),
|
|
27
|
+
[product]
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
return <View>{/* ... */}</View>
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Correct (lightweight list item):**
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
function ProductRow({ name, price, imageUrl }: Props) {
|
|
38
|
+
// Good: receives only primitives, minimal hooks
|
|
39
|
+
return (
|
|
40
|
+
<View>
|
|
41
|
+
<Image source={{ uri: imageUrl }} />
|
|
42
|
+
<Text>{name}</Text>
|
|
43
|
+
<Text>{price}</Text>
|
|
44
|
+
</View>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Move data fetching to parent:**
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
// Parent fetches all data once
|
|
53
|
+
function ProductList() {
|
|
54
|
+
const { data: products } = useQuery(['products'], fetchProducts)
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<LegendList
|
|
58
|
+
data={products}
|
|
59
|
+
renderItem={({ item }) => (
|
|
60
|
+
<ProductRow name={item.name} price={item.price} imageUrl={item.image} />
|
|
61
|
+
)}
|
|
62
|
+
/>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**For shared values, use Zustand selectors instead of Context:**
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
// Incorrect: Context causes re-render when any cart value changes
|
|
71
|
+
function ProductRow({ id, name }: Props) {
|
|
72
|
+
const { items } = useContext(CartContext)
|
|
73
|
+
const inCart = items.includes(id)
|
|
74
|
+
// ...
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Correct: Zustand selector only re-renders when this specific value changes
|
|
78
|
+
function ProductRow({ id, name }: Props) {
|
|
79
|
+
// use Set.has (created once at the root) instead of Array.includes()
|
|
80
|
+
const inCart = useCartStore((s) => s.items.has(id))
|
|
81
|
+
// ...
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Guidelines for list items:**
|
|
86
|
+
|
|
87
|
+
- No queries or data fetching
|
|
88
|
+
- No expensive computations (move to parent or memoize at parent level)
|
|
89
|
+
- Prefer Zustand selectors over React Context
|
|
90
|
+
- Minimize useState/useEffect hooks
|
|
91
|
+
- Pass pre-computed values as props
|
|
92
|
+
|
|
93
|
+
The goal: list items should be simple rendering functions that take props and
|
|
94
|
+
return JSX.
|