Universal model provides unified state management:
store, states, actions, selectors and services are the same for every UI framework, only view parts differ.
1. Create initial state
export interface Todo {
id: number;
name: string;
isDone: boolean;
}
export default {
todos: [] as Todo[],
shouldShowOnlyUnDoneTodos: false,
isFetchingTodos: false,
hasTodosFetchFailure: false
};
2. Create state selectors
const createTodoListStateSelectors = <T extends State>() => ({
shownTodos: (state: T) =>
state.todosState.todos.filter(
(todo: Todo) =>
(state.todosState.shouldShowOnlyUnDoneTodos && !todo.isDone) ||
!state.todosState.shouldShowOnlyUnDoneTodos
),
todoCount: (state: T) => state.todosState.todos.length,
unDoneTodoCount: (state: T) => state.todosState.todos.filter((todo: Todo) => !todo.isDone).length
});
3. Create a store (using initial states and selectors)
const initialState = {
todosState: createSubState(initialTodoListState)
};
export type State = typeof initialState;
const todoListStateSelectors = createTodoListStateSelectors<State>();
const selectors = combineSelectors<State, typeof todoListStateSelectors>(
todoListStateSelectors
);
export default createStore<State, typeof selectors>(initialState, selectors);
4. Create some actions
export async function fetchTodos(): Promise<void> {
const { todosState } = store.getState();
todosState.isFetchingTodos = true;
todosState.hasTodosFetchFailure = false;
try {
todosState.todos = await todoService.tryFetchTodos();
} catch (error) {
todosState.hasTodosFetchFailure = true;
}
todosState.isFetchingTodos = false;
}
export function removeTodo(todoToRemove: Todo): void {
const { todosState } = store.getState();
todosState.todos = todosState.todos.filter((todo: Todo) => todo !== todoToRemove);
}
export function toggleIsDoneTodo(todo: Todo): void {
todo.isDone = !todo.isDone;
}
5. Create a view (Angular)
@Component({
selector: 'app-todo-list-view',
template: `
<div>
<div *ngIf="todosState.isFetchingTodos">Fetching todos...</div>
<div *ngIf="todosState.hasTodosFetchFailure; else todoList">Failed to fetch todos</div>
<ng-template #todoList>
<ul>
<li *ngFor="let todo of shownTodos">
<input id="todo.name" type="checkbox" [checked]="todo.isDone" (click)="toggleIsDoneTodo(todo)" />
<label for="todo.name">{{ todo.name }}</label>
<button (click)="removeTodo(todo)">Remove</button>
</li>
</ul>
</ng-template>
</div>
`,
styleUrls: []
})
export class TodoListComponent implements OnInit {
todosState: typeof initialTodosState;
shownTodos: Todo[];
toggleIsDoneTodo = toggleIsDoneTodo;
removeTodo = removeTodo;
constructor() {
const [{ todosState }, { shownTodos }] = store.getStateAndSelectors();
store.useStateAndSelectors(this, { todosState }, { shownTodos });
}
ngOnInit(): void {
fetchTodos();
}
}