Skip to content
Back to skills

React State Management

ASecurity

Domine o gerenciamento moderno de estado React com Redux Toolkit, Zustand, Jotai e React Query. Use ao configurar estado global, gerenciar estado do servidor ou escolher entre soluções de gerenciamento de estado.

  • 11 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 8, 2026
developmenttypescriptgoreactgitapi

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 8, 2026

npx -y skills add artubss/SKILLS-CLAUDE-CODE --skill react-state-management --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of React State Management?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for React State Management
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/artubss-react-state-management/badge)](https://www.skillsdirectory.com/skills/artubss-react-state-management)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: react-state-management
description: "Domine o gerenciamento moderno de estado React com Redux Toolkit, Zustand, Jotai e React Query. Use ao configurar estado global, gerenciar estado do servidor ou escolher entre soluções de gerenciamento de estado."
risk: unknown
source: community
date_added: "2026-02-27"
---

# Gerenciamento de Estado em React

Guia abrangente de padrões modernos de gerenciamento de estado React, desde estado local do componente até stores globais e sincronização de estado do servidor.

## Não use essa skill quando

- A tarefa for não relacionada a gerenciamento de estado em React
- Você precisar de um domínio ou ferramenta diferente fora do escopo

## Instruções

- Esclareça objetivos, restrições e inputs necessários.
- Aplique as melhores práticas relevantes e valide os resultados.
- Forneça passos acionáveis e verificação.
- Se exemplos detalhados forem necessários, abra `resources/implementation-playbook.md`.

## Use essa skill quando

- Configurar gerenciamento de estado global em uma aplicação React
- Escolher entre Redux Toolkit, Zustand ou Jotai
- Gerenciar estado do servidor com React Query ou SWR
- Implementar atualizações otimistas
- Debugar problemas relacionados a estado
- Migrar de Redux legado para padrões modernos

## Conceitos Principais

### 1. Categorias de Estado

| Tipo | Descrição | Soluções |
|------|-----------|----------|
| **Estado Local** | Específico do componente, estado de UI | useState, useReducer |
| **Estado Global** | Compartilhado entre componentes | Redux Toolkit, Zustand, Jotai |
| **Estado do Servidor** | Dados remotos, cache | React Query, SWR, RTK Query |
| **Estado de URL** | Parâmetros de rota, busca | React Router, nuqs |
| **Estado de Formulário** | Valores de entrada, validação | React Hook Form, Formik |

### 2. Critérios de Seleção

```
Aplicação pequena, estado simples → Zustand ou Jotai
Aplicação grande, estado complexo → Redux Toolkit
Muita interação com servidor → React Query + estado cliente leve
Atualizações atômicas/granulares → Jotai
```

## Início Rápido

### Zustand (Mais Simples)

```typescript
// store/useStore.ts
import { create } from 'zustand'
import { devtools, persist } from 'zustand/middleware'

interface AppState {
  user: User | null
  theme: 'light' | 'dark'
  setUser: (user: User | null) => void
  toggleTheme: () => void
}

export const useStore = create<AppState>()(
  devtools(
    persist(
      (set) => ({
        user: null,
        theme: 'light',
        setUser: (user) => set({ user }),
        toggleTheme: () => set((state) => ({
          theme: state.theme === 'light' ? 'dark' : 'light'
        })),
      }),
      { name: 'app-storage' }
    )
  )
)

// Uso em componente
function Header() {
  const { user, theme, toggleTheme } = useStore()
  return (
    <header className={theme}>
      {user?.name}
      <button onClick={toggleTheme}>Toggle Theme</button>
    </header>
  )
}
```

## Padrões

### Padrão 1: Redux Toolkit com TypeScript

```typescript
// store/index.ts
import { configureStore } from '@reduxjs/toolkit'
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import userReducer from './slices/userSlice'
import cartReducer from './slices/cartSlice'

export const store = configureStore({
  reducer: {
    user: userReducer,
    cart: cartReducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        ignoredActions: ['persist/PERSIST'],
      },
    }),
})

export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch

// Hooks tipados
export const useAppDispatch: () => AppDispatch = useDispatch
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
```

```typescript
// store/slices/userSlice.ts
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit'

interface User {
  id: string
  email: string
  name: string
}

interface UserState {
  current: User | null
  status: 'idle' | 'loading' | 'succeeded' | 'failed'
  error: string | null
}

const initialState: UserState = {
  current: null,
  status: 'idle',
  error: null,
}

export const fetchUser = createAsyncThunk(
  'user/fetchUser',
  async (userId: string, { rejectWithValue }) => {
    try {
      const response = await fetch(`/api/users/${userId}`)
      if (!response.ok) throw new Error('Falha ao buscar usuário')
      return await response.json()
    } catch (error) {
      return rejectWithValue((error as Error).message)
    }
  }
)

const userSlice = createSlice({
  name: 'user',
  initialState,
  reducers: {
    setUser: (state, action: PayloadAction<User>) => {
      state.current = action.payload
      state.status = 'succeeded'
    },
    clearUser: (state) => {
      state.current = null
      state.status = 'idle'
    },
  },
  extraReducers: (builder) => {
    builder
      .addCase(fetchUser.pending, (state) => {
        state.status = 'loading'
        state.error = null
      })
      .addCase(fetchUser.fulfilled, (state, action) => {
        state.status = 'succeeded'
        state.current = action.payload
      })
      .addCase(fetchUser.rejected, (state, action) => {
        state.status = 'failed'
        state.error = action.payload as string
      })
  },
})

export const { setUser, clearUser } = userSlice.actions
export default userSlice.reducer
```

### Padrão 2: Zustand com Slices (Escalável)

```typescript
// store/slices/createUserSlice.ts
import { StateCreator } from 'zustand'

export interface UserSlice {
  user: User | null
  isAuthenticated: boolean
  login: (credentials: Credentials) => Promise<void>
  logout: () => void
}

export const createUserSlice: StateCreator<
  UserSlice & CartSlice, // Tipo de store combinado
  [],
  [],
  UserSlice
> = (set, get) => ({
  user: null,
  isAuthenticated: false,
  login: async (credentials) => {
    const user = await authApi.login(credentials)
    set({ user, isAuthenticated: true })
  },
  logout: () => {
    set({ user: null, isAuthenticated: false })
    // Pode acessar outras slices
    // get().clearCart()
  },
})

// store/index.ts
import { create } from 'zustand'
import { createUserSlice, UserSlice } from './slices/createUserSlice'
import { createCartSlice, CartSlice } from './slices/createCartSlice'

type StoreState = UserSlice & CartSlice

export const useStore = create<StoreState>()((...args) => ({
  ...createUserSlice(...args),
  ...createCartSlice(...args),
}))

// Subscrições seletivas (previne re-renders desnecessários)
export const useUser = () => useStore((state) => state.user)
export const useCart = () => useStore((state) => state.cart)
```

### Padrão 3: Jotai para Estado Atômico

```typescript
// atoms/userAtoms.ts
import { atom } from 'jotai'
import { atomWithStorage } from 'jotai/utils'

// Atom básico
export const userAtom = atom<User | null>(null)

// Atom derivado (computado)
export const isAuthenticatedAtom = atom((get) => get(userAtom) !== null)

// Atom com persistência em localStorage
export const themeAtom = atomWithStorage<'light' | 'dark'>('theme', 'light')

// Atom assíncrono
export const userProfileAtom = atom(async (get) => {
  const user = get(userAtom)
  if (!user) return null
  const response = await fetch(`/api/users/${user.id}/profile`)
  return response.json()
})

// Atom somente escrita (ação)
export const logoutAtom = atom(null, (get, set) => {
  set(userAtom, null)
  set(cartAtom, [])
  localStorage.removeItem('token')
})

// Uso
function Profile() {
  const [user] = useAtom(userAtom)
  const [, logout] = useAtom(logoutAtom)
  const [profile] = useAtom(userProfileAtom) // Suspense habilitado

  return (
    <Suspense fallback={<Skeleton />}>
      <ProfileContent profile={profile} onLogout={logout} />
    </Suspense>
  )
}
```

### Padrão 4: React Query para Estado do Servidor

```typescript
// hooks/useUsers.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'

// Factory de chaves de query
export const userKeys = {
  all: ['users'] as const,
  lists: () => [...userKeys.all, 'list'] as const,
  list: (filters: UserFilters) => [...userKeys.lists(), filters] as const,
  details: () => [...userKeys.all, 'detail'] as const,
  detail: (id: string) => [...userKeys.details(), id] as const,
}

// Hook de busca
export function useUsers(filters: UserFilters) {
  return useQuery({
    queryKey: userKeys.list(filters),
    queryFn: () => fetchUsers(filters),
    staleTime: 5 * 60 * 1000, // 5 minutos
    gcTime: 30 * 60 * 1000, // 30 minutos (anteriormente cacheTime)
  })
}

// Hook de usuário único
export function useUser(id: string) {
  return useQuery({
    queryKey: userKeys.detail(id),
    queryFn: () => fetchUser(id),
    enabled: !!id, // Não buscar se não houver id
  })
}

// Mutation com atualização otimista
export function useUpdateUser() {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: updateUser,
    onMutate: async (newUser) => {
      // Cancelar refetches pendentes
      await queryClient.cancelQueries({ queryKey: userKeys.detail(newUser.id) })

      // Snapshot do valor anterior
      const previousUser = queryClient.getQueryData(userKeys.detail(newUser.id))

      // Atualizar otimisticamente
      queryClient.setQueryData(userKeys.detail(newUser.id), newUser)

      return { previousUser }
    },
    onError: (err, newUser, context) => {
      // Reverter em caso de erro
      queryClient.setQueryData(
        userKeys.detail(newUser.id),
        context?.previousUser
      )
    },
    onSettled: (data, error, variables) => {
      // Refetch após mutation
      queryClient.invalidateQueries({ queryKey: userKeys.detail(variables.id) })
    },
  })
}
```

### Padrão 5: Combinando Estado Cliente + Servidor

```typescript
// Zustand para estado cliente
const useUIStore = create<UIState>((set) => ({
  sidebarOpen: true,
  modal: null,
  toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
  openModal: (modal) => set({ modal }),
  closeModal: () => set({ modal: null }),
}))

// React Query para estado do servidor
function Dashboard() {
  const { sidebarOpen, toggleSidebar } = useUIStore()
  const { data: users, isLoading } = useUsers({ active: true })
  const { data: stats } = useStats()

  if (isLoading) return <DashboardSkeleton />

  return (
    <div className={sidebarOpen ? 'with-sidebar' : ''}>
      <Sidebar open={sidebarOpen} onToggle={toggleSidebar} />
      <main>
        <StatsCards stats={stats} />
        <UserTable users={users} />
      </main>
    </div>
  )
}
```

## Melhores Práticas

### Faça
- **Coloque o estado próximo** - Mantenha o estado o mais próximo possível de onde é usado
- **Use seletores** - Previne re-renders desnecessários com subscrições seletivas
- **Normalize dados** - Achate estruturas aninhadas para facilitar atualizações
- **Tipifique tudo** - Cobertura completa de TypeScript previne erros de runtime
- **Separe responsabilidades** - Estado do servidor (React Query) vs estado cliente (Zustand)

### Não faça
- **Não globalize excessivamente** - Nem tudo precisa estar em estado global
- **Não duplique estado do servidor** - Deixe o React Query gerenciar
- **Não mutue diretamente** - Sempre use atualizações imutáveis
- **Não armazene dados derivados** - Compute-os ao invés
- **Não misture paradigmas** - Escolha uma solução primária por categoria

## Guias de Migração

### De Redux Legado para RTK

```typescript
// Antes (Redux legado)
const ADD_TODO = 'ADD_TODO'
const addTodo = (text) => ({ type: ADD_TODO, payload: text })
function todosReducer(state = [], action) {
  switch (action.type) {
    case ADD_TODO:
      return [...state, { text: action.payload, completed: false }]
    default:
      return state
  }
}

// Depois (Redux Toolkit)
const todosSlice = createSlice({
  name: 'todos',
  initialState: [],
  reducers: {
    addTodo: (state, action: PayloadAction<string>) => {
      // Immer permite "mutações"
      state.push({ text: action.payload, completed: false })
    },
  },
})
```

## Recursos

- [Documentação Redux Toolkit](https://redux-toolkit.js.org/)
- [Zustand GitHub](https://github.com/pmndrs/zustand)
- [Documentação Jotai](https://jotai.org/)
- [TanStack Query](https://tanstack.com/query)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…