❓ Qué problema resuelve
- Estado compartido: Múltiples componentes necesitan acceder al mismo estado
- Flujo de datos complejo: Datos fluyendo en múltiples direcciones
- Debugging difícil: Difícil rastrear cambios de estado
- Efectos secundarios: Mutaciones impredecibles del estado
🔧 Cómo funciona
Implementa un flujo unidireccional de datos donde las acciones desencadenan cambios de estado a través de reducers, y los componentes se suscriben a estos cambios.
📊 Diagrama de Arquitectura Flux
graph LR
A[Action] --> B[Dispatcher]
B --> C[Store]
C --> D[View]
D --> A
subgraph "Flux Flow"
E[User Interaction] --> F[Action Creator]
F --> G[Action]
G --> H[Dispatcher]
H --> I[Store]
I --> J[View Update]
end
📊 Diagrama de Arquitectura Redux
graph TB
subgraph "Redux Architecture"
A[UI Component] --> B[Action Creator]
B --> C[Action]
C --> D[Reducer]
D --> E[Store]
E --> F[State]
F --> A
G[Middleware] --> D
C --> G
end
subgraph "Redux DevTools"
H[Time Travel]
I[State Inspector]
J[Action Logger]
end
E --> H
E --> I
E --> J
🎯 Flux Pattern
📊 Diagrama de Clases
classDiagram
class Action {
+type: string
+payload: any
+meta?: any
}
class Dispatcher {
+dispatch(action): void
+register(callback): string
+unregister(id): void
}
class Store {
+getState(): any
+emitChange(): void
+addChangeListener(callback): void
+removeChangeListener(callback): void
}
class View {
+render(): JSX.Element
+componentDidMount(): void
+onChange(): void
}
Action --> Dispatcher
Dispatcher --> Store
Store --> View
View --> Action
🏗️ Estructura de Carpetas Flux
src/
├── actions/
│ ├── UserActions.js
│ ├── ProductActions.js
│ └── ActionTypes.js
├── dispatcher/
│ └── AppDispatcher.js
├── stores/
│ ├── UserStore.js
│ ├── ProductStore.js
│ └── BaseStore.js
├── components/
│ ├── UserList.jsx
│ ├── ProductList.jsx
│ └── App.jsx
└── utils/
└── WebAPIUtils.js
🎯 Redux Pattern
📊 Diagrama de Clases
classDiagram
class Action {
+type: string
+payload?: any
+error?: boolean
+meta?: any
}
class ActionCreator {
+createUser(user): Action
+updateUser(id, user): Action
+deleteUser(id): Action
}
class Reducer {
+function(state, action): newState
}
class Store {
+getState(): any
+dispatch(action): void
+subscribe(listener): function
}
class Selector {
+getUsers(state): User[]
+getUserById(state, id): User
+getLoadingState(state): boolean
}
ActionCreator --> Action
Action --> Reducer
Reducer --> Store
Store --> Selector
🏗️ Estructura de Carpetas Redux
src/
├── store/
│ ├── index.js # Store configuration
│ └── rootReducer.js
├── features/
│ ├── users/
│ │ ├── userSlice.js # Redux Toolkit slice
│ │ ├── userActions.js # Async actions
│ │ ├── userSelectors.js # Selectors
│ │ ├── userSagas.js # Side effects
│ │ └── components/
│ │ ├── UserList.jsx
│ │ └── UserForm.jsx
│ └── products/
│ ├── productSlice.js
│ ├── productActions.js
│ └── components/
├── shared/
│ ├── middleware/
│ │ ├── apiMiddleware.js
│ │ └── loggerMiddleware.js
│ └── utils/
│ └── apiClient.js
└── hooks/
├── useAppDispatch.js
├── useAppSelector.js
└── useAsyncAction.js
🔄 Flujo de Datos Detallado
Redux Flow
sequenceDiagram
participant C as Component
participant AC as Action Creator
participant M as Middleware
participant R as Reducer
participant S as Store
participant Sel as Selector
C->>AC: User clicks button
AC->>M: dispatch(action)
M->>M: Process side effects
M->>R: Forward action
R->>R: Calculate new state
R->>S: Return new state
S->>S: Update state
S->>Sel: State changed
Sel->>C: Re-render with new data
Async Actions Flow
sequenceDiagram
participant C as Component
participant T as Thunk/Saga
participant API as API
participant R as Reducer
participant S as Store
C->>T: dispatch(asyncAction)
T->>R: dispatch(LOADING)
T->>API: API call
API-->>T: Response/Error
alt Success
T->>R: dispatch(SUCCESS, data)
else Error
T->>R: dispatch(ERROR, error)
end
R->>S: Update state
S->>C: Re-render
🎨 Implementación por Framework
React + Redux
classDiagram
class UserComponent {
+useSelector(selectUsers)
+useDispatch()
+handleCreateUser()
+handleUpdateUser()
}
class userSlice {
+name: 'users'
+initialState: UserState
+reducers: UserReducers
+extraReducers: AsyncReducers
}
class userActions {
+fetchUsers(): AsyncThunk
+createUser(user): AsyncThunk
+updateUser(id, user): AsyncThunk
}
class userSelectors {
+selectAllUsers(state): User[]
+selectUserById(state, id): User
+selectUsersLoading(state): boolean
}
UserComponent --> userSlice
UserComponent --> userActions
UserComponent --> userSelectors
Angular + NgRx
classDiagram
class UserComponent {
+users$: Observable~User[]~
+loading$: Observable~boolean~
+onCreateUser(user): void
}
class UserActions {
+loadUsers(): Action
+loadUsersSuccess(users): Action
+loadUsersFailure(error): Action
}
class UserReducer {
+function(state, action): UserState
}
class UserEffects {
+loadUsers$: Observable~Action~
+createUser$: Observable~Action~
}
class UserSelectors {
+selectAllUsers: MemoizedSelector
+selectUsersLoading: MemoizedSelector
}
UserComponent --> UserActions
UserActions --> UserReducer
UserActions --> UserEffects
UserComponent --> UserSelectors
Vue + Vuex
classDiagram
class UserComponent {
+computed: mapGetters
+methods: mapActions
+created(): void
}
class UserModule {
+namespaced: true
+state: UserState
+getters: UserGetters
+mutations: UserMutations
+actions: UserActions
}
class UserService {
+fetchUsers(): Promise~User[]~
+createUser(user): Promise~User~
}
UserComponent --> UserModule
UserModule --> UserService
🧪 Testing Strategies
Redux Testing
graph TB
A[Action Tests] --> B[Test Action Creators]
C[Reducer Tests] --> D[Test State Changes]
E[Selector Tests] --> F[Test Data Selection]
G[Component Tests] --> H[Mock Store]
I[Integration Tests] --> J[Real Store]
Testing Pyramid
graph TB
A[E2E Tests] --> B[User Workflows]
C[Integration Tests] --> D[Connected Components]
E[Unit Tests] --> F[Reducers]
E --> G[Actions]
E --> H[Selectors]
E --> I[Components]
🔧 Middleware y Side Effects
Middleware Chain
graph LR
A[Action] --> B[Logger Middleware]
B --> C[Thunk Middleware]
C --> D[API Middleware]
D --> E[Reducer]
E --> F[Store]
Side Effects Patterns
classDiagram
class ReduxThunk {
+function(dispatch, getState): any
}
class ReduxSaga {
+function*(): Generator
+takeEvery(pattern, saga): void
+call(fn, ...args): Effect
+put(action): Effect
}
class RTKQuery {
+createApi(config): ApiSlice
+fetchBaseQuery(config): BaseQuery
}
ReduxThunk --> Store
ReduxSaga --> Store
RTKQuery --> Store
🎯 Patrones Avanzados
Normalized State
graph TB
subgraph "Normalized State Structure"
A[entities] --> B[users]
A --> C[posts]
A --> D[comments]
B --> E[byId: {1: user1, 2: user2}]
B --> F[allIds: [1, 2]]
C --> G[byId: {1: post1, 2: post2}]
C --> H[allIds: [1, 2]]
end
Feature-Based Architecture
graph TB
subgraph "Feature Slices"
A[authSlice] --> B[login, logout, register]
C[usersSlice] --> D[CRUD operations]
E[postsSlice] --> F[CRUD + comments]
end
subgraph "Shared"
G[apiSlice] --> H[RTK Query endpoints]
I[uiSlice] --> J[loading, errors, modals]
end
🎨 Mejores Prácticas
Estado Inmutable
graph TB
A[Current State] --> B[Action]
B --> C[Reducer]
C --> D[New State]
E[❌ Mutation] --> F[state.users.push(newUser)]
G[✅ Immutable] --> H[...state, users: [...state.users, newUser]]
Action Design
classDiagram
class GoodAction {
+type: "users/userAdded"
+payload: User
+meta: {timestamp: Date}
}
class BadAction {
+type: "ADD_USER"
+user: User
+otherData: any
+randomProperty: any
}
note for GoodAction "FSA compliant\nPredictable structure\nNamespaced type"
note for BadAction "Non-standard structure\nUnpredictable properties\nGeneric type"
🔄 Migración y Evolución
From Flux to Redux
graph LR
A[Multiple Stores] --> B[Single Store]
C[Event Emitters] --> D[Subscriptions]
E[Imperative] --> F[Functional]
G[Dispatcher] --> H[Middleware]
Modern Redux (RTK)
graph TB
A[Redux Toolkit] --> B[createSlice]
A --> C[createAsyncThunk]
A --> D[RTK Query]
A --> E[Immer Integration]
F[Benefits] --> G[Less Boilerplate]
F --> H[Better DevEx]
F --> I[Built-in Best Practices]
🎯 Cuándo Usar Redux/Flux
✅ Usar cuando:
- Estado compartido entre muchos componentes
- Lógica de estado compleja
- Debugging y time-travel necesarios
- Aplicaciones grandes con múltiples desarrolladores
- Efectos secundarios complejos
❌ No usar cuando:
- Aplicaciones pequeñas con estado local
- Prototipado rápido
- Estado principalmente en servidor
- Componentes independientes
- Overhead no justificado
📊 Comparación con Otras Soluciones
Aspecto | Redux | Context API | Zustand | Recoil |
---|---|---|---|---|
Curva de aprendizaje | Alta | Baja | Baja | Media |
Boilerplate | Alto | Bajo | Muy bajo | Bajo |
DevTools | Excelente | Básico | Bueno | Bueno |
Performance | Buena | Variable | Buena | Excelente |
Ecosistema | Muy grande | Nativo | Pequeño | Creciendo |