# Configuración de Store

Esta página cubre cómo configurar tu store de Redux, configurar hooks tipados y organizar la estructura del proyecto.

## Estructura de carpetas

Los proyectos DEBEN seguir esta estructura para consistencia y mantenibilidad:

```
src/
  app/
    store.ts              # Configuración del store
    hooks.ts              # Hooks tipados (useAppDispatch/useAppSelector)
  shared/
    types/                # DTOs (API), modelos de dominio, mappers
    utils/                # Utilidades compartidas
  services/
    client.ts             # Cliente base de RTK Query
    baseQuery.ts          # Consulta base con auth/chainId/retry
  features/
    user/
      user.client.ts      # Endpoints de RTK Query
      user.slice.ts       # Slice de estado de la UI
      user.selectors.ts   # Selectores memoizados
      __tests__/          # Tests
    land/
      land.client.ts
      land.slice.ts
      land.selectors.ts
    credits/
      credits.client.ts
      credits.slice.ts
      credits.selectors.ts
```

### Principios de organización de carpetas

* **Basado en funcionalidades** - Agrupar por dominio de negocio, no por rol técnico
* **Co-ubicación** - Mantener el código relacionado junto
* **Separación clara** - Distinguir entre datos remotos (`.client.ts`) y estado local (`.slice.ts`)

## Configuración del store

El store DEBE configurarse con el `configureStore` de RTK e incluir todo el middleware necesario.

### Configuración básica del 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 del cliente RTK Query (DEBE incluirse)
    [client.reducerPath]: client.reducer,
    
    // Feature slices
    user: userReducer,
    land: landReducer,
    credits: creditsReducer,
  },
  
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        // Ignorar redux-persist u otras rutas no serializables conocidas
        ignoredActions: ['persist/PERSIST', 'persist/REHYDRATE'],
        ignoredPaths: ['register'],
      },
    }).concat(client.middleware), // DEBE incluir el middleware de RTK Query
    
  devTools: process.env.NODE_ENV !== 'production',
});

// Exportar tipos para usar en toda la app
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
```

### Opciones de configuración

#### Comprobación de serializabilidad

El `serializableCheck` el middleware valida que todo el estado sea serializable. Configúralo para ignorar excepciones conocidas:

```tsx
serializableCheck: {
  // Acciones a ignorar
  ignoredActions: [
    'persist/PERSIST',
    'persist/REHYDRATE',
  ],
  // Rutas de estado a ignorar
  ignoredPaths: ['register', 'socket.connection'],
}
```

{% hint style="warning" %}
**Nunca deshabilitar `serializableCheck` completamente.** En su lugar, configura excepciones y mantiene datos no serializables fuera de Redux.
{% endhint %}

#### Dev Tools

Habilita Redux DevTools en desarrollo para depuración:

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

Para depuración en producción (si es necesario), usa una configuración específica:

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

## Hooks tipados

Crea versiones tipadas de `useDispatch` y `useSelector` para mejor inferencia de tipos y experiencia de desarrollo.

### Configuración de hooks

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

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

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

### Uso en componentes

```tsx
// ❌ Malo: Usando hooks estándar
import { useDispatch, useSelector } from 'react-redux';
import { RootState } from '@/app/store';

function Component() {
  const dispatch = useDispatch(); // Sin inferencia de tipos
  const user = useSelector((state: RootState) => state.user); // Tipado manual
}

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

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

### Beneficios de los hooks tipados

1. **Autocompletado** - IntelliSense completo para la forma del estado
2. **Seguridad de tipos** - Capturar errores en tiempo de compilación
3. **Refactorización** - Renombrar propiedades de estado con confianza
4. **Menos código repetitivo** - No es necesario especificar `RootState` repetidamente

## Configuración del Provider

Envuelve tu app con el Provider de 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>
);
```

## Configuración de entorno

Configura opciones específicas por entorno:

```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';

// Ajustes de RTK Query
export const RTK_QUERY_CONFIG = {
  keepUnusedDataFor: 60, // segundos
  refetchOnFocus: true,
  refetchOnReconnect: true,
  refetchOnMountOrArgChange: 30, // segundos
} as const;
```

## Definiciones de tipos

Crea definiciones de tipos compartidas para consistencia:

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

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

/** Error común de la API */
export interface ApiError {
  message: string;
  code: string;
  details?: Record<string, any>;
}
```

```tsx
// src/shared/types/domain.types.ts
/** Modelo de dominio User */
export interface User {
  id: string;
  address: string;
  name?: string;
  avatar?: string;
  createdAt: string;
}

/** Modelo de dominio Parcel */
export interface Parcel {
  id: string;
  x: number;
  y: number;
  owner: string;
  name?: string;
}
```

## Múltiples instancias del store

Para testing o micro-frontends, puede que necesites múltiples instancias del store:

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

export function createStore(preloadedState?: Partial<RootState>) {
  return configureStore({
    reducer: {
      // ... reducers
    },
    preloadedState,
    // ... otra configuración
  });
}

// Instancia de store por defecto
export const store = createStore();

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

## Buenas prácticas

### 1. Store único

Siempre usa una única instancia de store por aplicación:

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

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

### 2. Carga perezosa de reducers

Para code splitting, inyecta reducers dinámicamente:

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

const staticReducers = {
  user: userReducer,
};

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

// En el store
let currentReducers = createReducer();

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

### 3. Hot Module Replacement

Habilita HMR para reducers en desarrollo:

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

### 4. Persistencia de estado

Al usar redux-persist, configúralo con cuidado:

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

const persistConfig = {
  key: 'root',
  storage,
  // Solo persistir slices específicos
  whitelist: ['user', 'preferences'],
  // Nunca persistir la caché de RTK Query
  blacklist: ['client'],
};

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

## Siguientes pasos

* Aprende sobre [RTK Query](/contributor/contributor-es/guias-para-colaboradores/estandares-de-ui/rtk-query.md) para la obtención de datos
* Entender [Gestión de Estado](/contributor/contributor-es/guias-para-colaboradores/estandares-de-ui/state-management.md) para estado local
* Revisar [Patrones de Componentes](/contributor/contributor-es/guias-para-colaboradores/estandares-de-ui/component-patterns.md) para ejemplos de uso


---

# Agent Instructions: Querying This Documentation

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

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.decentraland.org/contributor/contributor-es/guias-para-colaboradores/estandares-de-ui/store-setup.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
