Research: Redux Toolkit 2.x selected for state management
- Completed Redux vs Zustand vs Jotai comparison - Redux Toolkit selected (9.2/10 score) - Best for complex state (family structure, permissions) - Best for offline sync (RTK Query, optimistic updates) - Largest ecosystem (most resources, tutorials, examples) - Best developer experience (time-travel debugging) - 100% code sharing between React Native and React Trade-offs: - More boilerplate (clearer structure) - Steeper learning curve (better patterns) - Larger bundle 60KB vs 3KB (negligible impact) Updated tech stack decisions and README Next: Authentication system design (JWT with recovery phrases)
This commit is contained in:
parent
735df1f15d
commit
195ba2ec4e
4 changed files with 911 additions and 30 deletions
750
thoughts/research/2026-02-14-state-management-research.md
Normal file
750
thoughts/research/2026-02-14-state-management-research.md
Normal file
|
|
@ -0,0 +1,750 @@
|
|||
# State Management Research: Redux vs Zustand vs Jotai
|
||||
|
||||
**Date**: 2026-02-14
|
||||
**Focus**: State management for Normogen's encrypted health data platform
|
||||
**Platform**: React Native + React (70-80% code sharing)
|
||||
|
||||
---
|
||||
|
||||
## Research Context
|
||||
|
||||
Normogen's state management requirements:
|
||||
|
||||
- **70-80% code sharing** between React Native and React
|
||||
- **Complex state**: Family structure, multi-person profiles, permissions
|
||||
- **Encrypted data**: All health data encrypted client-side
|
||||
- **Offline synchronization**: Mobile-first with sync to server
|
||||
- **TypeScript**: Full TypeScript codebase
|
||||
- **Bundle size**: Mobile app needs to stay lightweight
|
||||
- **Health sensors**: Real-time health data updates
|
||||
|
||||
---
|
||||
|
||||
## Options Overview
|
||||
|
||||
### 1. Redux (with Redux Toolkit)
|
||||
|
||||
**Description**: Industry-standard state management with predictable state container
|
||||
|
||||
**Latest Version**: Redux Toolkit 2.x (2024)
|
||||
|
||||
**Architecture**:
|
||||
- Centralized store (single source of truth)
|
||||
- Reducers for state updates (pure functions)
|
||||
- Actions for state changes (dispatched events)
|
||||
- Selectors for accessing state (memoized)
|
||||
- Middleware for side effects (thunk, saga)
|
||||
|
||||
**Pros for Normogen**:
|
||||
- **Mature ecosystem**: Largest community, extensive documentation
|
||||
- **TypeScript support**: Excellent with Redux Toolkit
|
||||
- **DevTools**: Best-in-class debugging tools
|
||||
- **Middleware**: Great for async operations, sync logic
|
||||
- **Normalization**: Ideal for family structure, profiles
|
||||
- **Time-travel debugging**: Replay any state change
|
||||
- **Code sharing**: 100% compatible with React Native + React
|
||||
- **Battle-tested**: Used by large apps (Twitter, Uber, Facebook)
|
||||
|
||||
**Cons for Normogen**:
|
||||
- **Boilerplate**: More setup than Zustand/Jotai
|
||||
- **Bundle size**: Larger than Zustand/Jotai (47KB minzipped)
|
||||
- **Learning curve**: More concepts to learn (actions, reducers, middleware)
|
||||
- **Overkill**: May be complex for smaller use cases
|
||||
|
||||
**Bundle Size Impact**:
|
||||
- Redux Toolkit: ~47KB minzipped
|
||||
- React-Redux: ~13KB minzipped
|
||||
- Total: ~60KB minzipped
|
||||
|
||||
**TypeScript Support**: Excellent (Redux Toolkit built with TypeScript)
|
||||
|
||||
**Code Sharing**: 100% between React Native and React
|
||||
|
||||
---
|
||||
|
||||
### 2. Zustand
|
||||
|
||||
**Description**: Minimal, fast state management using React hooks
|
||||
|
||||
**Latest Version**: Zustand 4.x (2024)
|
||||
|
||||
**Architecture**:
|
||||
- Hook-based store (useStore)
|
||||
- Actions are functions (no dispatch, no reducers)
|
||||
- No providers (no need to wrap app)
|
||||
- Built-in devtools, persistence, middleware
|
||||
|
||||
**Pros for Normogen**:
|
||||
- **Simple**: Minimal API, easy to learn
|
||||
- **Boilerplate**: Much less than Redux
|
||||
- **Bundle size**: Tiny (~3KB minzipped)
|
||||
- **Performance**: No re-renders with selectors
|
||||
- **TypeScript**: Excellent support
|
||||
- **Code sharing**: 100% compatible with React Native + React
|
||||
- **Modern**: Built for React hooks, no legacy patterns
|
||||
- **DevTools**: Built-in debugging
|
||||
- **Persistence**: Built-in middleware for async storage
|
||||
- **No providers**: Simpler app structure
|
||||
|
||||
**Cons for Normogen**:
|
||||
- **Smaller ecosystem**: Less mature than Redux
|
||||
- **Less battle-tested**: Fewer large-scale deployments
|
||||
- **No time-travel debugging**: Cannot replay state changes easily
|
||||
- **Normalization**: No built-in state normalization
|
||||
- **Async middleware**: Less mature than Redux
|
||||
|
||||
**Bundle Size Impact**:
|
||||
- Zustand core: ~3KB minzipped
|
||||
- Immer included: Built-in
|
||||
- Total: ~3KB minzipped
|
||||
|
||||
**TypeScript Support**: Excellent (built with TypeScript)
|
||||
|
||||
**Code Sharing**: 100% between React Native and React
|
||||
|
||||
---
|
||||
|
||||
### 3. Jotai
|
||||
|
||||
**Description**: Primitive and flexible state management for React
|
||||
|
||||
**Latest Version**: Jotai 2.x (2024)
|
||||
|
||||
**Architecture**:
|
||||
- Atomic state (small, independent pieces of state)
|
||||
- Hook-based (atom families, useAtom)
|
||||
- Bottom-up (compose state from primitives)
|
||||
- No providers (optional, but recommended)
|
||||
|
||||
**Pros for Normogen**:
|
||||
- **Minimal**: Smallest bundle size (~3KB)
|
||||
- **Flexible**: Can model any state architecture
|
||||
- **Performance**: Only re-renders components using specific atoms
|
||||
- **TypeScript**: Excellent support
|
||||
- **Code sharing**: 100% compatible with React Native + React
|
||||
- **Modern**: Latest React patterns, hooks-based
|
||||
- **Composable**: Build complex state from simple atoms
|
||||
- **DevTools**: Built-in debugging
|
||||
- **React Concurrent**: Optimized for React 18 concurrent features
|
||||
|
||||
**Cons for Normogen**:
|
||||
- **Smallest ecosystem**: Less mature than Zustand
|
||||
- **Newest**: Less proven in production
|
||||
- **Learning curve**: Atomic model may be unfamiliar
|
||||
- **Less structure**: No enforced patterns (flexibility can be double-edged)
|
||||
- **Fewer tutorials**: Harder to find best practices
|
||||
- **No time-travel debugging**: Cannot replay state changes
|
||||
|
||||
**Bundle Size Impact**:
|
||||
- Jotai core: ~3KB minzipped
|
||||
- Immer included: Built-in
|
||||
- Total: ~3KB minzipped
|
||||
|
||||
**TypeScript Support**: Excellent (built with TypeScript)
|
||||
|
||||
**Code Sharing**: 100% between React Native and React
|
||||
|
||||
---
|
||||
|
||||
## Comparison Matrix
|
||||
|
||||
| Criterion | Redux Toolkit | Zustand | Jotai |
|
||||
|-----------|---------------|----------|--------|
|
||||
| **Bundle Size** | 60KB | 3KB | 3KB |
|
||||
| **Boilerplate** | High | Low | Low |
|
||||
| **Learning Curve** | Steep | Gentle | Moderate |
|
||||
| **Ecosystem** | Largest | Large | Medium |
|
||||
| **TypeScript** | Excellent | Excellent | Excellent |
|
||||
| **DevTools** | Best | Good | Good |
|
||||
| **Time-Travel Debug** | Yes | No | No |
|
||||
| **Code Sharing** | 100% | 100% | 100% |
|
||||
| **Maturity** | Very High | High | Medium |
|
||||
| **Performance** | Good | Excellent | Excellent |
|
||||
| **Normalization** | Built-in | Manual | Manual |
|
||||
| **Async Middleware** | Excellent | Good | Good |
|
||||
| **Battle-Tested** | Excellent | Good | Fair |
|
||||
| **Community** | Largest | Large | Medium |
|
||||
| **Documentation** | Excellent | Good | Good |
|
||||
| **Mobile Support** | Excellent | Excellent | Excellent |
|
||||
|
||||
---
|
||||
|
||||
## Normogen-Specific Analysis
|
||||
|
||||
### 1. Code Sharing (Critical: 70-80%)
|
||||
|
||||
All three options (Redux, Zustand, Jotai) are 100% compatible with both React Native and React.
|
||||
|
||||
**Verdict**: Tie (all excellent)
|
||||
|
||||
---
|
||||
|
||||
### 2. Bundle Size (Critical: Mobile App)
|
||||
|
||||
- **Redux Toolkit**: 60KB (larger impact on mobile app size)
|
||||
- **Zustand**: 3KB (negligible impact)
|
||||
- **Jotai**: 3KB (negligible impact)
|
||||
|
||||
**Analysis**:
|
||||
- For a mobile app, 60KB is significant (may affect download size)
|
||||
- However, total React Native app will be 50-100MB anyway
|
||||
- 60KB is <0.1% of total app size
|
||||
- Performance impact is negligible on modern phones
|
||||
|
||||
**Verdict**: Zustand/Jotai win, but not critical
|
||||
|
||||
---
|
||||
|
||||
### 3. TypeScript Support (Critical: Full TypeScript Codebase)
|
||||
|
||||
All three options have excellent TypeScript support:
|
||||
|
||||
- **Redux Toolkit**: Built with TypeScript, full type safety
|
||||
- **Zustand**: Built with TypeScript, full type safety
|
||||
- **Jotai**: Built with TypeScript, full type safety
|
||||
|
||||
**Verdict**: Tie (all excellent)
|
||||
|
||||
---
|
||||
|
||||
### 4. Complex State (Critical: Family Structure, Multi-Person Profiles)
|
||||
|
||||
**Use Case**: Managing family structure with parents, children, elderly, and access permissions
|
||||
|
||||
**Redux Toolkit (Best)**:
|
||||
- Built-in normalization (Redux Toolkit createEntityAdapter)
|
||||
- Enforced patterns (reducers, actions)
|
||||
- Predictable state updates
|
||||
- Easy to debug complex state interactions
|
||||
- Time-travel debugging for complex state trees
|
||||
|
||||
**Zustand (Good)**:
|
||||
- No built-in normalization
|
||||
- Can manually normalize state
|
||||
- More flexibility, but less structure
|
||||
- Harder to debug complex state interactions
|
||||
|
||||
**Jotai (Fair)**:
|
||||
- Atomic state model
|
||||
- Can normalize, but no built-in patterns
|
||||
- Most flexibility, but least structure
|
||||
- Hardest to debug complex state interactions
|
||||
|
||||
**Verdict**: Redux Toolkit wins for complex state
|
||||
|
||||
---
|
||||
|
||||
### 5. Offline Synchronization (Critical: Mobile-First)
|
||||
|
||||
**Use Case**: Sync encrypted health data to server when offline, handle conflicts
|
||||
|
||||
**Redux Toolkit (Best)**:
|
||||
- **Redux Toolkit RTK Query**: Excellent for server state
|
||||
- Built-in optimistic updates
|
||||
- Automatic cache invalidation
|
||||
- Background sync support
|
||||
- Battle-tested offline patterns
|
||||
- Redux Saga for complex async flows
|
||||
|
||||
**Zustand (Good)**:
|
||||
- Can build sync with middleware
|
||||
- No built-in server state management
|
||||
- Need to build sync logic manually
|
||||
- Can use RTK Query with Zustand (but why not use Redux?)
|
||||
|
||||
**Jotai (Fair)**:
|
||||
- Can build sync with atoms
|
||||
- No built-in server state management
|
||||
- Need to build sync logic manually
|
||||
- Least proven for offline sync
|
||||
|
||||
**Verdict**: Redux Toolkit wins for offline sync
|
||||
|
||||
---
|
||||
|
||||
### 6. Encrypted Data Caching (High Priority)
|
||||
|
||||
**Use Case**: Cache encrypted health data locally, decrypt on demand
|
||||
|
||||
**Redux Toolkit (Best)**:
|
||||
- Normalized state ideal for caching
|
||||
- Selectors for efficient data access
|
||||
- Persist entire store to AsyncStorage
|
||||
- Redux Persist middleware built-in
|
||||
|
||||
**Zustand (Good)**:
|
||||
- Built-in persistence middleware (zustand/persist)
|
||||
- Can cache encrypted data
|
||||
- Manual normalization
|
||||
|
||||
**Jotai (Good)**:
|
||||
- Can persist atoms to AsyncStorage
|
||||
- Can cache encrypted data
|
||||
- Manual normalization
|
||||
|
||||
**Verdict**: Redux Toolkit wins, but Zustand is close second
|
||||
|
||||
---
|
||||
|
||||
### 7. Learning Curve (Medium Priority)
|
||||
|
||||
**Team Considerations**:
|
||||
- Team knows JavaScript/TypeScript
|
||||
- Team may not know React Native deeply
|
||||
- Need to ship mobile app quickly
|
||||
|
||||
**Redux Toolkit**: Steep learning curve
|
||||
- Concepts: actions, reducers, middleware, selectors, normalization
|
||||
- More boilerplate to write
|
||||
- More patterns to learn
|
||||
|
||||
**Zustand**: Gentle learning curve
|
||||
- Simple API: createStore, useStore
|
||||
- Less boilerplate
|
||||
- Easier to get started
|
||||
|
||||
**Jotai**: Moderate learning curve
|
||||
- Concept: atomic state (may be unfamiliar)
|
||||
- Less boilerplate than Redux
|
||||
- More structure than Zustand
|
||||
|
||||
**Verdict**: Zustand wins for ease of learning
|
||||
|
||||
---
|
||||
|
||||
### 8. Developer Experience (Medium Priority)
|
||||
|
||||
**Redux Toolkit (Best)**:
|
||||
- Best DevTools (Redux DevTools)
|
||||
- Time-travel debugging
|
||||
- Predictable state updates
|
||||
- Easy to debug
|
||||
- More verbose, but clearer
|
||||
|
||||
**Zustand (Good)**:
|
||||
- Good DevTools (Zustand DevTools)
|
||||
- Less code to write
|
||||
- Simpler, but less structure
|
||||
- Easier to write, harder to debug
|
||||
|
||||
**Jotai (Good)**:
|
||||
- Good DevTools (Jotai DevTools)
|
||||
- Less code to write
|
||||
- Most flexible, but least structure
|
||||
- Hardest to debug
|
||||
|
||||
**Verdict**: Redux Toolkit wins for debugging
|
||||
|
||||
---
|
||||
|
||||
### 9. Ecosystem Maturity (Medium Priority)
|
||||
|
||||
**Redux Toolkit**: Very mature
|
||||
- Largest ecosystem
|
||||
- Most tutorials, blog posts, StackOverflow answers
|
||||
- Most libraries (Redux Toolkit, RTK Query, Redux Saga)
|
||||
- Most production deployments
|
||||
- Easy to hire developers
|
||||
|
||||
**Zustand**: Mature
|
||||
- Large ecosystem
|
||||
- Good tutorials, blog posts, StackOverflow answers
|
||||
- Many production deployments
|
||||
- Easy to hire developers
|
||||
|
||||
**Jotai**: Less mature
|
||||
- Smaller ecosystem
|
||||
- Fewer tutorials, blog posts, StackOverflow answers
|
||||
- Fewer production deployments
|
||||
- Harder to hire developers
|
||||
|
||||
**Verdict**: Redux Toolkit wins for ecosystem maturity
|
||||
|
||||
---
|
||||
|
||||
### 10. Health Sensor Integration (Low Priority)
|
||||
|
||||
**Use Case**: Real-time health data updates (steps, heart rate, sleep)
|
||||
|
||||
All three options handle real-time updates equally well:
|
||||
- React Native Health provides callbacks
|
||||
- Updates dispatched to store
|
||||
- No significant difference
|
||||
|
||||
**Verdict**: Tie (all good)
|
||||
|
||||
---
|
||||
|
||||
## Scorecard
|
||||
|
||||
| Criterion | Redux Toolkit | Zustand | Jotai | Weight |
|
||||
|-----------|---------------|----------|--------|--------|
|
||||
| **Code Sharing** | 10 | 10 | 10 | Critical |
|
||||
| **Bundle Size** | 7 | 10 | 10 | Critical |
|
||||
| **TypeScript** | 10 | 10 | 10 | Critical |
|
||||
| **Complex State** | 10 | 7 | 5 | Critical |
|
||||
| **Offline Sync** | 10 | 7 | 5 | Critical |
|
||||
| **Encrypted Cache** | 10 | 8 | 8 | High |
|
||||
| **Learning Curve** | 6 | 9 | 8 | Medium |
|
||||
| **DevTools** | 10 | 7 | 7 | Medium |
|
||||
| **Ecosystem** | 10 | 8 | 6 | Medium |
|
||||
| **Health Sensors** | 10 | 10 | 10 | Low |
|
||||
| **Weighted Score** | 9.2 | 8.6 | 7.6 | - |
|
||||
|
||||
---
|
||||
|
||||
## Recommendation
|
||||
|
||||
### Primary Recommendation: **Redux Toolkit**
|
||||
|
||||
**Score**: 9.2/10
|
||||
|
||||
**Justification**:
|
||||
|
||||
1. **Complex State Management** (Critical)
|
||||
- Best for family structure, multi-person profiles, permissions
|
||||
- Built-in normalization (Redux Toolkit createEntityAdapter)
|
||||
- Predictable state updates
|
||||
- Time-travel debugging
|
||||
|
||||
2. **Offline Synchronization** (Critical)
|
||||
- RTK Query for server state management
|
||||
- Optimistic updates
|
||||
- Background sync support
|
||||
- Battle-tested offline patterns
|
||||
|
||||
3. **Ecosystem Maturity** (Medium)
|
||||
- Largest ecosystem
|
||||
- Most resources, tutorials, examples
|
||||
- Most production deployments
|
||||
- Easiest to hire developers
|
||||
|
||||
4. **Developer Experience** (Medium)
|
||||
- Best DevTools (Redux DevTools)
|
||||
- Time-travel debugging
|
||||
- Predictable state updates
|
||||
- Easy to debug
|
||||
|
||||
**Trade-offs**:
|
||||
- **More boilerplate**: More code to write, but clearer structure
|
||||
- **Steeper learning curve**: More concepts to learn, but better patterns
|
||||
- **Larger bundle**: 60KB vs 3KB (negligible impact on 50-100MB app)
|
||||
|
||||
**Verdict**: Redux Toolkit is the best choice for Normogen's complex state management needs
|
||||
|
||||
---
|
||||
|
||||
### Alternative Considered: Zustand
|
||||
|
||||
**Score**: 8.6/10
|
||||
|
||||
**Why Zustand is a strong alternative**:
|
||||
|
||||
1. **Simplicity**: Less boilerplate, easier to learn
|
||||
2. **Bundle Size**: 3KB vs 60KB (negligible impact)
|
||||
3. **Performance**: Excellent performance, no re-renders
|
||||
4. **Modern**: Built for React hooks, no legacy patterns
|
||||
|
||||
**Why Redux Toolkit wins**:
|
||||
|
||||
1. **Complex State**: Redux has built-in normalization, enforced patterns
|
||||
2. **Offline Sync**: RTK Query is excellent for server state
|
||||
3. **Ecosystem**: Larger ecosystem, more resources
|
||||
4. **Debugging**: Time-travel debugging for complex state trees
|
||||
|
||||
**When to use Zustand instead**:
|
||||
- If team has no Redux experience
|
||||
- If state is simpler (no family structure)
|
||||
- If development speed is more important than structure
|
||||
- If 60KB bundle size is critical (not the case here)
|
||||
|
||||
---
|
||||
## Technology Stack with Redux Toolkit
|
||||
|
||||
### Mobile (React Native)
|
||||
- **Framework**: React Native 0.73+
|
||||
- **Language**: TypeScript
|
||||
- **State Management**: Redux Toolkit 2.x
|
||||
- **Data Fetching**: RTK Query 2.x
|
||||
- **Async Thunks**: Redux Toolkit createAsyncThunk
|
||||
- **Persistence**: Redux Persist 6.x (AsyncStorage)
|
||||
- **DevTools**: Redux DevTools (React Native Debugger)
|
||||
|
||||
### Web (React)
|
||||
- **Framework**: React 18+
|
||||
- **Language**: TypeScript
|
||||
- **State Management**: Redux Toolkit 2.x
|
||||
- **Data Fetching**: RTK Query 2.x
|
||||
- **Async Thunks**: Redux Toolkit createAsyncThunk
|
||||
- **Persistence**: Redux Persist 6.x (localStorage)
|
||||
- **DevTools**: Redux DevTools (Browser Extension)
|
||||
|
||||
### Shared (Monorepo)
|
||||
- **Language**: TypeScript
|
||||
- **State Management**: Redux Toolkit 2.x
|
||||
- **Reducers**: Shared reducers (user, family, encryption)
|
||||
- **Selectors**: Shared selectors (memoized with Reselect)
|
||||
- **Types**: Shared TypeScript types
|
||||
- **Actions**: Shared action creators
|
||||
- **Middleware**: Shared middleware (logging, crash reporting)
|
||||
|
||||
---
|
||||
|
||||
## Implementation Example: Family Structure
|
||||
|
||||
### State Slice (Redux Toolkit)
|
||||
|
||||
```typescript
|
||||
// shared/store/slices/familySlice.ts
|
||||
import { createEntityAdapter, createSlice } from '@reduxjs/toolkit';
|
||||
import type { EntityState } from '@reduxjs/toolkit';
|
||||
|
||||
export interface FamilyMember {
|
||||
id: string;
|
||||
name: string;
|
||||
role: 'parent' | 'child' | 'elderly';
|
||||
permissions: string[];
|
||||
profileId: string;
|
||||
}
|
||||
|
||||
const familyAdapter = createEntityAdapter<FamilyMember>({
|
||||
selectId: (member) => member.id,
|
||||
sortComparer: (a, b) => a.name.localeCompare(b.name),
|
||||
});
|
||||
|
||||
interface FamilyState extends EntityState<FamilyMember> {
|
||||
currentFamilyId: string | null;
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
const initialState: FamilyState = {
|
||||
currentFamilyId: null,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
...familyAdapter.getInitialState(),
|
||||
};
|
||||
|
||||
export const familySlice = createSlice({
|
||||
name: 'family',
|
||||
initialState,
|
||||
reducers: {
|
||||
familyMemberAdded: familyAdapter.addOne,
|
||||
familyMemberUpdated: familyAdapter.updateOne,
|
||||
familyMemberRemoved: familyAdapter.removeOne,
|
||||
setCurrentFamily: (state, action) => {
|
||||
state.currentFamilyId = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
familyMemberAdded,
|
||||
familyMemberUpdated,
|
||||
familyMemberRemoved,
|
||||
setCurrentFamily,
|
||||
} = familySlice.actions;
|
||||
|
||||
export const {
|
||||
selectAll: selectAllFamilyMembers,
|
||||
selectById: selectFamilyMemberById,
|
||||
selectIds: selectFamilyMemberIds,
|
||||
} = familyAdapter.getSelectors();
|
||||
|
||||
export default familySlice.reducer;
|
||||
```
|
||||
|
||||
### Async Thunk (Offline Sync)
|
||||
|
||||
```typescript
|
||||
// shared/store/thunks/syncFamilyMembers.ts
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
import { apiClient } from '../api/client';
|
||||
import { decryptFamilyMembers, encryptFamilyMembers } from '../crypto';
|
||||
|
||||
export const syncFamilyMembers = createAsyncThunk(
|
||||
'family/syncFamilyMembers',
|
||||
async (_, { getState }) => {
|
||||
const state = getState() as RootState;
|
||||
const { lastSyncTime } = state.family;
|
||||
|
||||
// Fetch encrypted family members from server
|
||||
const response = await apiClient.get('/api/family/sync', {
|
||||
params: { since: lastSyncTime },
|
||||
});
|
||||
|
||||
// Decrypt on client-side
|
||||
const familyMembers = await decryptFamilyMembers(
|
||||
response.data.encryptedData,
|
||||
state.encryption.key,
|
||||
);
|
||||
|
||||
return familyMembers;
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### RTK Query (Data Fetching)
|
||||
|
||||
```typescript
|
||||
// shared/store/api/healthDataApi.ts
|
||||
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
|
||||
import { apiClient } from '../api/client';
|
||||
|
||||
export const healthDataApi = createApi({
|
||||
reducerPath: 'healthDataApi',
|
||||
baseQuery: fetchBaseQuery({
|
||||
baseUrl: '/api',
|
||||
prepareHeaders: (headers, { getState }) => {
|
||||
const token = (getState() as RootState).auth.token;
|
||||
if (token) headers.set('authorization', `Bearer ${token}`);
|
||||
return headers;
|
||||
},
|
||||
}),
|
||||
tagTypes: ['HealthData'],
|
||||
endpoints: (builder) => ({
|
||||
getHealthData: builder.query<EncryptedHealthData, string>({
|
||||
query: (profileId) => `/health-data/${profileId}`,
|
||||
providesTags: ['HealthData'],
|
||||
}),
|
||||
updateHealthData: builder.mutation<void, UpdateHealthDataRequest>({
|
||||
query: ({ profileId, data }) => ({
|
||||
url: `/health-data/${profileId}`,
|
||||
method: 'POST',
|
||||
body: data,
|
||||
}),
|
||||
invalidatesTags: ['HealthData'],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const { useGetHealthDataQuery, useUpdateHealthDataMutation } = healthDataApi;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Proof of Concept Requirements
|
||||
|
||||
### Redux Toolkit POC
|
||||
|
||||
1. **Family Structure State**
|
||||
- Create family slice with normalized state
|
||||
- Add/update/remove family members
|
||||
- Manage permissions
|
||||
- Test selectors
|
||||
|
||||
2. **Offline Sync**
|
||||
- Implement RTK Query for server state
|
||||
- Implement optimistic updates
|
||||
- Test background sync
|
||||
- Handle conflicts
|
||||
|
||||
3. **Encrypted Data Caching**
|
||||
- Persist encrypted data to AsyncStorage
|
||||
- Decrypt on demand
|
||||
- Test performance with large datasets
|
||||
|
||||
4. **TypeScript Types**
|
||||
- Full type safety
|
||||
- Shared types between mobile and web
|
||||
- Test type inference
|
||||
|
||||
### Zustand Alternative POC
|
||||
|
||||
1. **Family Structure State**
|
||||
- Create family store with Zustand
|
||||
- Add/update/remove family members
|
||||
- Manage permissions
|
||||
- Test selectors
|
||||
|
||||
2. **Offline Sync**
|
||||
- Implement custom sync middleware
|
||||
- Test background sync
|
||||
- Handle conflicts
|
||||
|
||||
3. **Encrypted Data Caching**
|
||||
- Persist encrypted data to AsyncStorage
|
||||
- Decrypt on demand
|
||||
- Test performance with large datasets
|
||||
|
||||
---
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
### Redux Toolkit Risks
|
||||
|
||||
| Risk | Severity | Mitigation |
|
||||
|------|----------|------------|
|
||||
| Steep learning curve | Medium | Team training, good documentation |
|
||||
| More boilerplate | Low | Redux Toolkit reduces boilerplate significantly |
|
||||
| Larger bundle size | Low | 60KB is negligible for 50-100MB app |
|
||||
| Over-engineering | Low | Normogen has complex state needs |
|
||||
| Performance | Low | Redux is fast enough for health app |
|
||||
|
||||
### Zustand Risks
|
||||
|
||||
| Risk | Severity | Mitigation |
|
||||
|------|----------|------------|
|
||||
| Less structure | Medium | Need to enforce patterns manually |
|
||||
| Complex state harder | Medium | More difficult for family structure |
|
||||
| Offline sync less mature | Medium | Need to build sync logic manually |
|
||||
| Less ecosystem | Low | Zustand ecosystem is large enough |
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
### Primary Recommendation: Redux Toolkit 2.x
|
||||
|
||||
**Score**: 9.2/10
|
||||
|
||||
**Key Advantages**:
|
||||
|
||||
1. **Best for Complex State**: Family structure, multi-person profiles, permissions
|
||||
2. **Best for Offline Sync**: RTK Query, optimistic updates, background sync
|
||||
3. **Best Ecosystem**: Largest ecosystem, most resources, easiest to hire
|
||||
4. **Best Developer Experience**: Time-travel debugging, predictable state updates
|
||||
5. **TypeScript**: Excellent support, full type safety
|
||||
6. **Code Sharing**: 100% between React Native and React
|
||||
|
||||
**Trade-offs**:
|
||||
- More boilerplate: More code, but clearer structure
|
||||
- Steeper learning curve: More concepts, but better patterns
|
||||
- Larger bundle: 60KB vs 3KB (negligible impact)
|
||||
|
||||
**Verdict**: Redux Toolkit is the best choice for Normogen's encrypted health data platform
|
||||
|
||||
---
|
||||
|
||||
### Alternative: Zustand 4.x
|
||||
|
||||
**Score**: 8.6/10
|
||||
|
||||
**When to use Zustand instead**:
|
||||
- If team has no Redux experience
|
||||
- If state is simpler (no family structure)
|
||||
- If development speed is more important than structure
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Implement Redux Toolkit POC
|
||||
2. Create family structure state slice
|
||||
3. Implement RTK Query for server state
|
||||
4. Test offline synchronization
|
||||
5. Test encrypted data caching
|
||||
6. Evaluate developer experience
|
||||
7. Make final decision
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [Redux Toolkit Documentation](https://redux-toolkit.js.org/)
|
||||
- [RTK Query Documentation](https://redux-toolkit.js.org/rtk-query/overview)
|
||||
- [Redux Persist](https://github.com/rt2zz/redux-persist)
|
||||
- [Zustand Documentation](https://github.com/pmndrs/zustand)
|
||||
- [Jotai Documentation](https://jotai.org/)
|
||||
- [React Redux](https://react-redux.js.org/)
|
||||
- [Reselect](https://github.com/reduxjs/reselect)
|
||||
Loading…
Add table
Add a link
Reference in a new issue