Comparison with Other State Libraries
Inglorious Store combines the best aspects of Redux, Redux Toolkit, and game engines. Here's how it compares:
Feature Comparison
| Feature | Redux | RTK | Zustand | Jotai | Pinia | MobX | Inglorious |
|---|---|---|---|---|---|---|---|
| Boilerplate | ๐ด High | ๐ก Medium | ๐ข Low | ๐ข Low | ๐ก Medium | ๐ข Low | ๐ข Low |
| Multiple instances | ๐ด Manual | ๐ด Manual | ๐ด Manual | ๐ด Manual | ๐ก Medium | ๐ก Medium | ๐ข Built-in |
| Lifecycle events | ๐ด No | ๐ด No | ๐ด No | ๐ด No | ๐ด No | ๐ด No | ๐ข Yes |
| Async logic | ๐ก Thunks | ๐ก Complex | ๐ข Free | ๐ข Free | ๐ข Free | ๐ข Free | ๐ข Native |
| Redux DevTools | ๐ข Yes | ๐ข Yes | ๐ก Partial | ๐ก Partial | ๐ก Partial | ๐ข Yes | ๐ข Yes |
| Time-travel | ๐ข Yes | ๐ข Yes | ๐ด No | ๐ด No | ๐ด No | ๐ก Limited | ๐ข Yes |
| Testability | ๐ข Excellent | ๐ข Excellent | ๐ก Good | ๐ก Good | ๐ก Good | ๐ก Medium | ๐ข Excellent |
| Immutability | ๐ด Manual | ๐ข Immer | ๐ด Manual | ๐ด Manual | ๐ด Manual | ๐ด Manual | ๐ข Mutative |
Detailed Comparison
๐ฎ Entity Management
Redux/RTK: Managing multiple instances requires reshaping state or creating separate reducers:
const store = configureStore({
reducer: {
workTodos: todosReducer,
personalTodos: todosReducer,
},
})
// Adding a new list at runtime is complex
// Requires reducer refactoring or complex middleware
2
3
4
5
6
7
8
9
Inglorious Store: Entities are first-class citizens. Add instances at runtime with no code changes:
const entities = {
workTodos: { type: "TodoList", todos: [] },
personalTodos: { type: "TodoList", todos: [] },
}
// Adding new list at runtime
store.notify("add", { id: "projectTodos", type: "TodoList", todos: [] })
2
3
4
5
6
7
๐ฆ Boilerplate
Redux: Action creators + reducers + setup
// Action creators
export const addTodo = (text) => ({ type: "ADD_TODO", payload: text })
// Reducer
const todosReducer = (state = [], action) => {
switch (action.type) {
case "ADD_TODO":
return [...state, { text: action.payload, done: false }]
default:
return state
}
}
// Store
const store = createStore(todosReducer)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Redux Toolkit: Still boilerplate
const todosSlice = createSlice({
name: "todos",
initialState: [],
reducers: {
addTodo: (state, action) => {
state.push({ text: action.payload, done: false })
},
},
})
2
3
4
5
6
7
8
9
Inglorious Store: Define types and entities, done
const types = {
todoList: {
addTodo(entity, text) {
entity.todos.push({ text, done: false })
},
},
}
const entities = {
todos: { type: "TodoList", todos: [] },
}
2
3
4
5
6
7
8
9
10
11
โก Async Operations
Redux: Thunks with pending/fulfilled/rejected boilerplate
const fetchTodos = createAsyncThunk("todos/fetch", async (userId) => {
return fetch(`/api/users/${userId}/todos`).then((r) => r.json())
})
extraReducers: (builder) => {
builder
.addCase(fetchTodos.pending, (state) => {
state.loading = true
})
.addCase(fetchTodos.fulfilled, (state, action) => {
state.data = action.payload
state.loading = false
})
.addCase(fetchTodos.rejected, (state) => {
state.loading = false
})
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Inglorious Store: Async handlers with full control
const types = {
todoList: {
async fetchTodos(entity, userId, api) {
entity.loading = true
const data = await fetch(`/api/users/${userId}/todos`).then((r) =>
r.json(),
)
api.notify("fetchSuccess", data)
},
fetchSuccess(entity, data) {
entity.todos = data
entity.loading = false
},
},
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
๐ Event System
Redux: Single dispatch, implicit action chains
// One action dispatched, reducers spread across store
dispatch({ type: "ADD_TODO", payload: "Buy milk" })
// Hard to track what happens next
2
3
4
Inglorious Store: Explicit pub/sub with targeted events
// Broadcast to all handlers
store.notify("taskCompleted", "task123")
// Target specific entity
store.notify("#work:taskCompleted", "task123")
// Target specific type
store.notify("stats:taskCompleted", "task123")
2
3
4
5
6
7
8
โป๏ธ Lifecycle
Redux/RTK/Others: No built-in lifecycle
// Must manually handle setup/cleanup
// No pattern for entity creation/destruction
2
Inglorious Store: Built-in lifecycle events
const types = {
myType: {
create(entity) {
// Automatic on add
entity.createdAt = Date.now()
},
destroy(entity, api) {
// Automatic on remove
console.log(`Cleaning up ${entity.id}`)
},
},
}
store.notify("add", { id: "entity1", type: "MyType" })
store.notify("remove", "entity1")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
๐ Performance
| Library | Immutability Strategy | Rerender Cost |
|---|---|---|
| Redux | Manual spread operators | High |
| RTK | Immer | Medium |
| Zustand | Manual | Variable |
| Inglorious | Mutative | Low |
Inglorious Store uses Mutative for 10x faster immutability than Immer:
entity.value = 100 // Looks like mutation
// Actually immutable under the hood
// 10x faster than Immer
2
3
๐งช Testing
Redux Reducers:
const state = todosReducer([{ id: 1, text: "Test", done: false }], {
type: "ADD_TODO",
payload: "New todo",
})
expect(state[1].text).toBe("New todo")
2
3
4
5
Inglorious Store Handlers:
import { trigger } from "@inglorious/store/test"
const { entity } = trigger(
{ type: "TodoList", todos: [] },
todoListType.addTodo,
"New todo",
)
expect(entity.todos[0].text).toBe("New todo")
2
3
4
5
6
7
8
Both test easily, but Inglorious provides test utilities.
๐พ DevTools & Debugging
| Library | DevTools | Time Travel | Inspector |
|---|---|---|---|
| Redux | โ Full | โ Yes | โ Yes |
| RTK | โ Full | โ Yes | โ Yes |
| Zustand | โ ๏ธ Limited | โ No | โ ๏ธ Limited |
| Jotai | โ ๏ธ Limited | โ No | โ ๏ธ Limited |
| Inglorious | โ Full | โ Yes | โ Yes |
Inglorious Store provides full Redux DevTools support out of the box.
When to Use Each
Use Redux/RTK if:
- โ You need maximum ecosystem support
- โ You're working on a large team with Redux experience
- โ You want proven battle-tested patterns
- โ You'll benefit from entity-based architecture
- โ You need built-in lifecycle events
Use Inglorious Store if:
- โ You want minimal boilerplate
- โ You manage multiple entity instances
- โ You want integrated async and lifecycle handling
- โ You're starting a new project
- โ You like game engine patterns
Use Zustand/Jotai if:
- โ You want ultra-lightweight state
- โ You don't need Redux DevTools
- โ You're building a small app
- โ You need entity management patterns
- โ You want Redux compatibility
Use Pinia/MobX if:
- โ You're using Vue (Pinia)
- โ You want reactive, observable patterns (MobX)
- โ You need Redux compatibility
- โ You want game engine-inspired patterns
Migration Paths
From Redux โ Inglorious Store
- Drop-in replacement (same API with
react-redux) - Keep existing components unchanged
- Gradually convert slices to types
- Full guide available: Migrate from Redux
From RTK โ Inglorious Store
- Convert slices to types
- Convert thunks to async handlers
- Migration utilities provided
- Full guide available: Migrate from Redux
From Zustand/Jotai โ Inglorious Store
- Rewrite atoms as types
- Adopt entity-based patterns
- Gain DevTools and lifecycle benefits
Conclusion
Inglorious Store is the sweet spot for projects that need:
- Redux-like predictability and debugging
- Minimal boilerplate
- Entity management without reshaping state
- Built-in lifecycle handling
- Native async support
Choose Inglorious Store if you want the simplicity of game engines in your state management.
Inglorious Store