Universal model is a unified state management solution for the most popular modern Web UI frameworks: Angular, React, Svelte and Vue.
The Universal model is composed of a store, states, actions, selectors and services that are the same for every UI framework — only view parts differ.
Universal model is best demonstrated by an example.
export interface Todo {
id: number;
name: string;
isDone: boolean;
}
export default {
todos: [] as Todo[],
shouldShowOnlyUnDoneTodos: false,
isFetchingTodos: false,
hasTodosFetchFailure: false
};
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
});
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);
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.1 React version:
const TodoListView = () => {
const [{ todosState }, { shownTodos }] = store.getStateAndSelectors();
store.useStateAndSelectors([todosState], [shownTodos]);
useEffect(() => {
fetchTodos();
}, []);
let todoListContent;
if (todosState.isFetchingTodos) {
todoListContent = <div>Fetching todos...</div>;
} else if (todosState.hasTodosFetchFailure) {
todoListContent = <div>Failed to fetch todos</div>;
} else {
const todoListItems = shownTodos.value.map((todo: Todo) => (
<li key={todo.id}>
<input
id={todo.name}
type="checkbox"
defaultChecked={todo.isDone}
onChange={() => toggleIsDoneTodo(todo)}
/>
<label>
{todo.name}
</label>
<button onClick={() => removeTodo(todo)}>Remove</button>
</li>
));
todoListContent = <ul>{todoListItems}</ul>;
}
return (
<div>
{todoListContent}
</div>
);
};
5.2 Vue 3+ version:
<template>
<div>
<div v-if="todosState.isFetchingTodos">Fetching todos...</div>
<div v-else-if="todosState.hasTodosFetchFailure">Failed to fetch todos</div>
<ul v-else>
<li v-for="todo in shownTodos">
<input :id="todo.name" type="checkbox" :checked="todo.isDone" @click="toggleIsDoneTodo(todo)" />
<label :for="todo.name">{{ userName }}: {{ todo.name }}</label>
<button @click="removeTodo(todo)">Remove</button>
</li>
</ul>
</div>
</template>
<script lang="ts">
export default {
setup(): object {
const [{ todosState }, { shownTodos }] = store.getStateAndSelectors();
onMounted(() => {
fetchTodos();
});
return {
todosState,
shownTodos,
removeTodo,
toggleIsDoneTodo
};
}
};
</script>
For more information, API, and examples, visit:
Thank you for reading!
#angular #vue #react #svelte