Communitygithub.com

react-native-patterns

Padrões para apps React Native e Expo — navegação com Expo Router, separação de estado (servidor/cliente/rota/formulário), busca de dados com TanStack Query e Zod, listas performáticas, estilização com NativeWind/StyleSheet, APIs nativas e armazenamento seguro. Use ao construir ou editar telas, componentes, navegação ou camadas de dados React Native / Expo.

O que é react-native-patterns?

Este skill fornece padrões práticos para construir aplicativos React Native em produção usando Expo. Ele cobre navegação com Expo Router (validação de parâmetros de rota com Zod), separação clara de estado (dados do servidor com TanStack Query, estado do cliente com Zustand ou Jotai, estado de rota com parâmetros do Expo Router, estado de formulário com React Hook Form), listas virtualizadas (FlatList ou FlashList), estilização consistente (NativeWind ou StyleSheet), acesso a APIs nativas (câmera, localização, notificações) e armazenamento seguro (expo-secure-store para tokens). O skill assume o fluxo de trabalho gerenciado do Expo (Expo Router, EAS) na Nova Arquitetura (padrão desde SDK 55+). Ele é útil para desenvolvedores que querem evitar armadilhas comuns, como duplicar dados do servidor em um store cliente, confiar em parâmetros de deep link sem validação, usar ScrollView para listas grandes, armazenar tokens em AsyncStorage ou criar objetos de estilo inline. O conteúdo inclui exemplos de código completos e anti-padrões, alinhado com o conjunto de regras `rules/react-native/` do ECC.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/tree/main/skills/react-native-patterns

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Padrões React Native / Expo

Padrões práticos para construir aplicativos React Native em produção com Expo. Abrange navegação, estado, busca de dados, listas, estilização e APIs nativas. Combina com o conjunto de regras rules/react-native/: as regras dizem o que aplicar, este skill mostra como.

As bibliotecas mencionadas abaixo (NativeWind, Zustand/Jotai, TanStack Query) são opções comuns e bem estabelecidas mostradas para ilustração — os padrões importam mais do que o pacote específico, e qualquer equivalente funciona. Zod é usado para validação para manter consistência com as regras typescript/ existentes do ECC.

Estes padrões assumem o fluxo de trabalho gerenciado do Expo (Expo Router, EAS, módulos expo-*) na Nova Arquitetura (o padrão em SDKs recentes do Expo, obrigatório a partir do SDK 55+). Eles NÃO assumem o DOM do navegador — React Native não tem <div>, nem barra de URL, nem padrões de busca de dados da web.

Quando Ativar

Use este skill quando:

  • Construindo ou editando telas, componentes ou navegação React Native / Expo
  • Configurando roteamento com Expo Router (diretório app/ baseado em arquivos)
  • Decidindo onde o estado pertence (cache do servidor vs store do cliente vs parâmetros de rota vs formulário)
  • Conectando busca de dados com TanStack Query e validando respostas com Zod
  • Renderizando listas longas ou pesadas
  • Escolhendo ou aplicando uma abordagem de estilização (NativeWind ou StyleSheet)
  • Acessando APIs de dispositivos nativos (câmera, localização, notificações) ou armazenamento seguro
  • Revisando código RN para problemas específicos de mobile

NÃO use os padrões web/React-DOM aqui — URL-como-estado, <div> e SWR-para-navegador não se aplicam a React Native.

Conceitos Principais

Estrutura do projeto (Expo Router)

Roteamento baseado em arquivos sob app/. Mantenha os arquivos de rota enxutos: eles leem e validam parâmetros, depois delegam para um componente de tela que reside em components/ ou features/.

app/
  _layout.tsx          # pilha raiz
  (tabs)/
    _layout.tsx        # navegador de abas
    index.tsx          # Início
  user/[id].tsx        # rota dinâmica
components/
features/
  user/UserProfile.tsx

Navegação: validar parâmetros de rota

Deep links e rotas dinâmicas entregam strings não confiáveis. Valide-as com Zod antes de usar.

// app/user/[id].tsx
import { useLocalSearchParams, router } from 'expo-router'
import { z } from 'zod'
import { UserProfile } from '@/features/user/UserProfile'

const Params = z.object({ id: z.string().uuid() })

export default function UserRoute() {
  const parsed = Params.safeParse(useLocalSearchParams())
  if (!parsed.success) {
    router.replace('/not-found')
    return null
  }
  return <UserProfile userId={parsed.data.id} />
}

Estado: mantenha as preocupações separadas

Não duplique dados do servidor em um store do cliente. Cada preocupação tem seu próprio lar.

PreocupaçãoEscolhas comuns
Estado do servidor (dados remotos)uma biblioteca de cache do servidor (TanStack Query, SWR)
Estado do cliente/UIum store leve (Zustand, Jotai) ou Context
Estado de rota/navegaçãoParâmetros do Expo Router
Estado de formuláriouma biblioteca de formulários (ex.: React Hook Form) + validação de esquema
Segredos / tokensexpo-secure-store
Persistência não secretaAsyncStorage / MMKV

Prefira useState local até que o estado realmente precise ser compartilhado.

Busca de dados: uma biblioteca de cache + Zod

Use uma biblioteca de cache do servidor (TanStack Query, SWR) em vez de fetch-in-useEffect. Valide na fronteira e infira tipos do esquema. Trate os estados de carregamento, erro e vazio explicitamente. (Exemplo usa TanStack Query.)

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { z } from 'zod'

const User = z.object({ id: z.string(), email: z.string().email() })
type User = z.infer<typeof User>

export function useUser(id: string) {
  return useQuery({
    queryKey: ['user', id],
    queryFn: async (): Promise<User> => User.parse(await api.getUser(id)),
  })
}

export function useUpdateEmail(id: string) {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (email: string) => api.updateEmail(id, email),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['user', id] }),
  })
}

Listas: virtualize, nunca mapeie um array grande em um ScrollView

import { FlatList } from 'react-native'

<FlatList
  data={items}
  keyExtractor={(item) => item.id}
  renderItem={renderItem}          // memoizado
  initialNumToRender={10}
  windowSize={5}
/>

Use FlashList (Shopify) para listas grandes ou heterogêneas.

Estilização: escolha um sistema

StyleSheet.create() é a opção nativa do framework; bibliotecas de classes utilitárias (ex.: NativeWind) são uma alternativa comum. Escolha um e mantenha consistência. Nunca construa objetos de estilo inline no JSX em caminhos críticos.

// NativeWind
<View className="p-4 rounded-2xl bg-white">
  <Text className="text-base font-semibold">Hello</Text>
</View>

// StyleSheet
const styles = StyleSheet.create({ card: { padding: 16, borderRadius: 16, backgroundColor: '#fff' } })
<View style={styles.card}>...</View>

APIs nativas: envolva em hooks, limpe efeitos

Mantenha chamadas e assinaturas do Expo SDK dentro de hooks use*, não no JSX. Sempre faça a limpeza.

import { useEffect, useState } from 'react'
import * as Location from 'expo-location'

type LocationState =
  | { status: 'loading' }
  | { status: 'denied' }
  | { status: 'granted'; coords: Location.LocationObjectCoords }

export function useCurrentLocation() {
  // Acompanhe o status, não apenas as coordenadas — para que a UI possa distinguir "ainda carregando"
  // de "permissão negada" e mostrar uma mensagem acionável.
  const [state, setState] = useState<LocationState>({ status: 'loading' })

  useEffect(() => {
    let active = true
    ;(async () => {
      const { status } = await Location.requestForegroundPermissionsAsync()
      if (status !== 'granted') {
        if (active) setState({ status: 'denied' })
        return
      }
      const pos = await Location.getCurrentPositionAsync({})
      if (active) setState({ status: 'granted', coords: pos.coords })
    })()
    return () => { active = false }   // ignora resultado obsoleto após desmontagem
  }, [])

  return state
}

Armazenamento seguro para tokens

import * as SecureStore from 'expo-secure-store'

await SecureStore.setItemAsync('auth_token', token)   // Keychain / Keystore
const token = await SecureStore.getItemAsync('auth_token')

Exemplos de Código

Uma tela completa: rota → consulta → lista → estados

// app/(tabs)/orders.tsx
import { memo, useCallback } from 'react'
import { FlatList, Text, View } from 'react-native'
import { useQuery } from '@tanstack/react-query'
import { z } from 'zod'

const OrderSchema = z.object({ id: z.string(), total: z.number(), status: z.string() })
const OrdersSchema = z.array(OrderSchema)
type Order = z.infer<typeof OrderSchema>

function useOrders() {
  return useQuery({
    queryKey: ['orders'],
    queryFn: async () => OrdersSchema.parse(await api.listOrders()),
  })
}

// Memoizada para que sua referência seja estável entre renderizações (veja a orientação sobre listas).
const OrderRow = memo(function OrderRow({ item }: { item: Order }) {
  return (
    <View className="px-4 py-3 border-b border-neutral-200">
      <Text className="font-medium">#{item.id}</Text>
      <Text className="text-neutral-500">{item.status} · ${item.total}</Text>
    </View>
  )
})

export default function OrdersScreen() {
  const { data, isLoading, isError, refetch, isRefetching } = useOrders()
  const renderItem = useCallback(({ item }: { item: Order }) => <OrderRow item={item} />, [])

  if (isLoading) return <Centered><Text>Carregando…</Text></Centered>
  if (isError) return <Centered><Text accessibilityRole="alert">Não foi possível carregar os pedidos.</Text></Centered>
  if (!data?.length) return <Centered><Text>Nenhum pedido ainda.</Text></Centered>

  return (
    <FlatList
      data={data}
      keyExtractor={(o) => o.id}
      onRefresh={refetch}
      refreshing={isRefetching}
      renderItem={renderItem}
    />
  )
}

Um formulário: React Hook Form + resolvedor Zod

import { useForm, Controller } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
import { TextInput, Button, Text } from 'react-native'

const Schema = z.object({ email: z.string().email('Email inválido') })
type FormValues = z.infer<typeof Schema>

export function EmailForm({ onSubmit }: { onSubmit: (v: FormValues) => void }) {
  const { control, handleSubmit, formState: { errors } } = useForm<FormValues>({
    resolver: zodResolver(Schema),
    defaultValues: { email: '' },
  })

  return (
    <>
      <Controller
        control={control}
        name="email"
        render={({ field: { value, onChange, onBlur } }) => (
          <TextInput
            value={value}
            onChangeText={onChange}
            onBlur={onBlur}
            autoCapitalize="none"
            keyboardType="email-address"
            accessibilityLabel="Endereço de email"
          />
        )}
      />
      {errors.email && <Text accessibilityRole="alert">{errors.email.message}</Text>}
      <Button title="Salvar" onPress={handleSubmit(onSubmit)} />
    </>
  )
}

Anti-Padrões

// ERRADO: array grande mapeado dentro de um ScrollView (sem virtualização, travado, alta memória)
<ScrollView>{items.map((i) => <Row key={i.id} item={i} />)}</ScrollView>
// CERTO: FlatList / FlashList

// ERRADO: dados do servidor copiados para um store do cliente (duas fontes de verdade, dados obsoletos)
const useStore = create((set) => ({ users: [], setUsers: (u) => set({ users: u }) }))
useEffect(() => { getUsers().then(setUsers) }, [])
// CERTO: useQuery é dono do estado do servidor; derive o que precisar

// ERRADO: tokens no AsyncStorage (não criptografado)
await AsyncStorage.setItem('auth_token', token)
// CERTO: expo-secure-store

// ERRADO: confiar em parâmetros de deep link
const { id } = useLocalSearchParams(); fetchUser(id)
// CERTO: validar com Zod antes de usar

// ERRADO: objeto de estilo inline recriado a cada renderização em um caminho crítico
<View style={{ padding: 16, backgroundColor: '#fff' }} />
// CERTO: StyleSheet.create no escopo do módulo, ou className do NativeWind

// ERRADO: segredo real enviado no bundle
const STRIPE_SECRET = 'sk_live_...'
// CERTO: manter chamadas privilegiadas no servidor; enviar apenas chaves públicas protegidas por regras de backend

Melhores Práticas

  • Mantenha os arquivos de rota enxutos; coloque a lógica em componentes de tela e hooks use*.
  • Valide toda entrada externa (respostas de API, parâmetros de rota, payloads de push) com Zod.
  • Deixe o TanStack Query ser dono do estado do servidor; mantenha os stores do cliente pequenos.
  • Sempre renderize estados de carregamento, erro e vazio — nunca apenas um spinner sem fallback.
  • Virtualize listas; memoize renderItem; forneça um keyExtractor estável.
  • Use react-native-reanimated para animações (thread da UI); evite trabalho pesado na thread JS.
  • Armazene tokens em expo-secure-store; nunca confie no cliente para autorização.
  • Respeite áreas seguras, Dynamic Type e papéis/rótulos de acessibilidade desde o início.
  • Confirme a compatibilidade com a Nova Arquitetura para cada dependência nativa antes do lançamento.

Skills Relacionados

  • frontend-patterns — Padrões React/Next.js (web); útil para conceitos compartilhados do React, mas específicos do DOM.
  • coding-standards — Expressões idiomáticas TypeScript/JavaScript que se aplicam ao código RN.
  • tdd-workflow, e2e-testing — Processo de teste (use Jest + React Native Testing Library, Maestro/Detox para RN).
  • security-review — Lista de verificação de segurança geral que complementa a orientação sobre bundle/segredos do RN acima.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

Habilidades Relacionadas