> For the complete documentation index, see [llms.txt](https://docs.decentraland.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.decentraland.org/contributor/contributor-pt/guias-para-colaboradores/padroes-de-ui/store-setup.md).

# Configuração do Store

Esta página aborda como configurar sua Redux store, definir hooks tipados e organizar a estrutura do seu projeto.

## Estrutura de Pastas

Os projetos DEVEM seguir esta estrutura para consistência e manutenibilidade:

```
src/
  app/
    store.ts              # Configuração da Store
    hooks.ts              # Hooks tipados (useAppDispatch/useAppSelector)
  shared/
    types/                # DTOs (API), modelos de domínio, mapeadores
    utils/                # Utilitários compartilhados
  services/
    client.ts             # Cliente base do RTK Query
    baseQuery.ts          # Consulta base com auth/chainId/retry
  features/
    user/
      user.client.ts      # Endpoints do RTK Query
      user.slice.ts       # Slice de estado da UI
      user.selectors.ts   # Selectors memorizados
      __tests__/          # Testes
    land/
      land.client.ts
      land.slice.ts
      land.selectors.ts
    credits/
      credits.client.ts
      credits.slice.ts
      credits.selectors.ts
```

### Princípios de Organização de Pastas

* **Baseado em funcionalidades** - Agrupe por domínio de negócio, não por função técnica
* **Co-localização** - Mantenha o código relacionado junto
* **Separação clara** - Distinga entre dados remotos (`.client.ts`) e estado local (`.slice.ts`)

## Configuração da Store

A store DEVE ser configurada com o `configureStore` do RTK e incluir todos os middlewares necessários.

### Configuração Básica da Store

```tsx
// src/app/store.ts
import { configureStore } from '@reduxjs/toolkit';
import { client } from '@/services/client';
import userReducer from '@/features/user/user.slice';
import landReducer from '@/features/land/land.slice';
import creditsReducer from '@/features/credits/credits.slice';

export const store = configureStore({
  reducer: {
    // Reducer do cliente RTK Query (DEVE ser incluído)
    [client.reducerPath]: client.reducer,
    
    // Slices das funcionalidades
    user: userReducer,
    land: landReducer,
    credits: creditsReducer,
  },
  
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        // Ignore redux-persist ou outros caminhos conhecidos não serializáveis
        ignoredActions: ['persist/PERSIST', 'persist/REHYDRATE'],
        ignoredPaths: ['register'],
      },
    }).concat(client.middleware), // DEVE incluir o middleware do RTK Query
    
  devTools: process.env.NODE_ENV !== 'production',
});

// Exporte tipos para uso em todo o app
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
```

### Opções de Configuração

#### Verificação de serialização

O `serializableCheck` o middleware valida que todo o estado é serializável. Configure-o para ignorar exceções conhecidas:

```tsx
serializableCheck: {
  // Ações a ignorar
  ignoredActions: [
    'persist/PERSIST',
    'persist/REHYDRATE',
  ],
  // Caminhos do estado a ignorar
  ignoredPaths: ['register', 'socket.connection'],
}
```

{% hint style="warning" %}
**Nunca desative `serializableCheck` completamente.** Em vez disso, configure exceções e mantenha dados não serializáveis fora do Redux.
{% endhint %}

#### Ferramentas de Desenvolvimento

Ative o Redux DevTools em desenvolvimento para depuração:

```tsx
devTools: process.env.NODE_ENV !== 'production'
```

Para depuração em produção (se necessário), use uma configuração específica:

```tsx
devTools: process.env.NODE_ENV !== 'production' ? true : {
  name: 'Decentraland App',
  trace: false,
  traceLimit: 25,
}
```

## Hooks tipados

Crie versões tipadas de `useDispatch` e `useSelector` para melhor inferência de tipos e experiência do desenvolvedor.

### Configuração dos Hooks

```tsx
// src/app/hooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import type { RootState, AppDispatch } from './store';

// Hook dispatch tipado
export const useAppDispatch = () => useDispatch<AppDispatch>();

// Hook selector tipado
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
```

### Uso nos Componentes

```tsx
// ❌ Ruim: Usando hooks padrão
import { useDispatch, useSelector } from 'react-redux';
import { RootState } from '@/app/store';

function Component() {
  const dispatch = useDispatch(); // Sem inferência de tipos
  const user = useSelector((state: RootState) => state.user); // Tipagem manual
}

// ✅ Bom: Usando hooks tipados
import { useAppDispatch, useAppSelector } from '@/app/hooks';

function Component() {
  const dispatch = useAppDispatch(); // Tipado automaticamente
  const user = useAppSelector((state) => state.user); // RootState inferido
}
```

### Benefícios dos Hooks Tipados

1. **Autocompletar** - IntelliSense completo para a estrutura do estado
2. **Segurança de tipos** - Detecte erros em tempo de compilação
3. **Refatoração** - Renomeie propriedades do estado com confiança
4. **Menos boilerplate** - Não há necessidade de especificar `RootState` repetidamente

## Configuração do Provider

Envolva seu app com o Provider do Redux:

### Next.js App Router

```tsx
// app/providers.tsx
'use client';

import { Provider } from 'react-redux';
import { store } from '@/app/store';

export function Providers({ children }: { children: React.ReactNode }) {
  return <Provider store={store}>{children}</Provider>;
}
```

```tsx
// app/layout.tsx
import { Providers } from './providers';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}
```

### Next.js Pages Router

```tsx
// pages/_app.tsx
import { Provider } from 'react-redux';
import { store } from '@/app/store';
import type { AppProps } from 'next/app';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <Provider store={store}>
      <Component {...pageProps} />
    </Provider>
  );
}
```

### React (Vite/CRA)

```tsx
// main.tsx / index.tsx
import { Provider } from 'react-redux';
import { store } from '@/app/store';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);
```

## Configuração do Ambiente

Configure as definições específicas do ambiente:

```tsx
// src/config/constants.ts
export const API_URL = process.env.NEXT_PUBLIC_API_URL || 'https://api.decentraland.org';
export const WS_URL = process.env.NEXT_PUBLIC_WS_URL || 'wss://api.decentraland.org';
export const CHAIN_ID = process.env.NEXT_PUBLIC_CHAIN_ID || '1';

// Configurações do RTK Query
export const RTK_QUERY_CONFIG = {
  keepUnusedDataFor: 60, // segundos
  refetchOnFocus: true,
  refetchOnReconnect: true,
  refetchOnMountOrArgChange: 30, // segundos
} as const;
```

## Definições de Tipo

Crie definições de tipo compartilhadas para consistência:

```tsx
// src/shared/types/api.types.ts
/** Wrapper de resposta da API */
export interface ApiResponse<T> {
  ok: boolean;
  data: T;
  error?: string;
}

/** Resposta paginada */
export interface PaginatedResponse<T> {
  items: T[];
  total: number;
  page: number;
  limit: number;
}

/** Erro comum da API */
export interface ApiError {
  message: string;
  code: string;
  details?: Record<string, any>;
}
```

```tsx
// src/shared/types/domain.types.ts
/** Modelo de domínio do usuário */
export interface User {
  id: string;
  address: string;
  name?: string;
  avatar?: string;
  createdAt: string;
}

/** Modelo de domínio do parcel */
export interface Parcel {
  id: string;
  x: number;
  y: number;
  owner: string;
  name?: string;
}
```

## Múltiplas Instâncias da Store

Para testes ou micro-frontends, você pode precisar de múltiplas instâncias da store:

```tsx
// src/app/store.ts
import { configureStore } from '@reduxjs/toolkit';

export function createStore(preloadedState?: Partial<RootState>) {
  return configureStore({
    reducer: {
      // ... reducers
    },
    preloadedState,
    // ... outra configuração
  });
}

// Instância padrão da store
export const store = createStore();

export type AppStore = ReturnType<typeof createStore>;
export type RootState = ReturnType<AppStore['getState']>;
export type AppDispatch = AppStore['dispatch'];
```

## Melhores práticas

### 1. Store Única

Use sempre uma única instância da store por aplicação:

```tsx
// ✅ Bom: Uma store
export const store = configureStore({ ... });

// ❌ Ruim: Múltiplas stores
export const userStore = configureStore({ ... });
export const cartStore = configureStore({ ... });
```

### 2. Carregamento Preguiçoso de Reducers

Para divisão de código, injete reducers dinamicamente:

```tsx
import { combineReducers } from '@reduxjs/toolkit';

const staticReducers = {
  user: userReducer,
};

export function createReducer(asyncReducers = {}) {
  return combineReducers({
    ...staticReducers,
    ...asyncReducers,
  });
}

// Na store
let currentReducers = createReducer();

export function injectReducer(key: string, reducer: Reducer) {
  currentReducers = createReducer({ [key]: reducer });
  store.replaceReducer(currentReducers);
}
```

### 3. Hot Module Replacement

Ative o HMR para reducers em desenvolvimento:

```tsx
if (process.env.NODE_ENV === 'development' && module.hot) {
  module.hot.accept('./reducer', () => {
    const newRootReducer = require('./reducer').default;
    store.replaceReducer(newRootReducer);
  });
}
```

### 4. Persistência do Estado

Ao usar redux-persist, configure com cuidado:

```tsx
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';

const persistConfig = {
  key: 'root',
  storage,
  // Persistir apenas slices específicas
  whitelist: ['user', 'preferences'],
  // Nunca persista o cache do RTK Query
  blacklist: ['client'],
};

const persistedReducer = persistReducer(persistConfig, rootReducer);
```

## Próximos passos

* Saiba mais sobre [RTK Query](/contributor/contributor-pt/guias-para-colaboradores/padroes-de-ui/rtk-query.md) para busca de dados
* Entenda [Gerenciamento de State](/contributor/contributor-pt/guias-para-colaboradores/padroes-de-ui/state-management.md) para estado local
* Rever [Padrões de Components](/contributor/contributor-pt/guias-para-colaboradores/padroes-de-ui/component-patterns.md) para exemplos de uso


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.decentraland.org/contributor/contributor-pt/guias-para-colaboradores/padroes-de-ui/store-setup.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
