Після тривалого використання нашого застосунку, ми виявили, що він ідеально підходить не тільки для покупок, а ще для списку справ, про котрі потрібно не забути, тому було вирішено добавити підтримку груп.
Спершу розробимо модель групи:
const GroupModel = types.model({
id: types.string,
title: types.string,
todos: types.array(TodoModel),
})
.actions((self) => ({
// для того щоб мати змогу додавати в групу задачу
addTodo(todo) {
self.todos.unshift(todo);
},
}));
Тепер було б добре мати модель, де ми будемо зберігати всі наші групи:
const GroupListModel = types.model({
list: types.array(GroupModel),
})
.actions((self) => ({
// для того щоб мати змогу додавати групу
add(title) {
const group = {
id: uuid(),
title,
};
self.list.unshift(group);
},
}));
А для того щоб це все зібрати до купи, при використанні mobx-state-tree прийнято робити модель, яка буде групувати всі інші моделі і буде добавимо RootModel, яка буде мати в собі як TodoListModel, так і GroupListModel:
const RootModel = types.model({
// тут optional використовується для того, щоб моделі автоматично
// ініціалізувались із пустим об'єктом як снепшот при ініціалізації
// моделі RootModel
// так як і в цьому випадку MST за нас робить під капотом `TodoListModel.create`
todos: types.optional(TodoListModel, {}),
groups: types.optional(GroupListModel, {}),
})
Давайте спробуємо це все запустити:
// ініціалізуємо наш рут-стор
const rootStore = RootModel.create({});
// логуємо все
autorun(() => prettyPrint(rootStore));
// додаємо задачу
rootStore.todos.add('Яйця');
const todo = rootStore.todos.list[0];
// створюємо групу
rootStore.groups.add('Покупки');
const group = rootStore.groups.list[0];
// додаємо задачу в групу
group.addTodo(todo);
І бачимо помилку:
Cannot add an object to a state tree if it is already part of the same or another state tree. Tried to assign an object to '/groups/list/0/todos/0', but it lives already at '/todos/list/0'
MST не дає нам додавати двічі ту ж задачу в дерево, кожна модель має бути представлена єдиним екземпляром.
Проте проблему можна вирішити, використавши утилітку, яку надає нам mobx-state-tree – clone
:
// клонуємо задачу.
// ця операція створює лише новий екземпляр моделі в дереві — ноду
// тобто контент може бути ідентичний, але зараз все працює
const todoClone = clone(todo);
// додаємо клоновану задачу в групу
group.addTodo(todoClone);
Останнім результатом autorun
повинен бути json, схожий на цей:
{
"todos": {
"list": [
{
"id": "7db4a95d-60fd-4db8-a2d6-d1c1b07af818",
"title": "Яйця",
"isCompleted": false,
"isFavorite": false
}
]
},
"groups": {
"list": [
{
"id": "be80e8de-7dda-491d-8449-3691007bcd8c",
"title": "Покупки",
"todos": [
{
"id": "7db4a95d-60fd-4db8-a2d6-d1c1b07af818",
"title": "Яйця",
"isCompleted": false,
"isFavorite": false
}
]
}
]
}
}
А тепер давайте спробуємо відмітити наші задачу як виконану в загальному списку todos.list[0]
та добавимо в обрану ту, що в groups.list[0].todos[0]
:
// ...решту дій з попереднього прокладу
const todoInGroup = rootStore.groups.list[0].todos[0];
todoInGroup.toggleCompleted();
const todoInList = rootStore.todos.list[0];
todoInList.toggleFavorite();
prettyPrint(rootStore);
І бачимо таку картину:
{
"todos": {
"list": [
{
"id": "a8f549bf-c73a-44b5-9209-43a9760f3d81",
"title": "Яйця",
"isCompleted": false,
"isFavorite": true
}
]
},
"groups": {
"list": [
{
"id": "1a0d0cd3-261a-4639-9187-c38de2a9273d",
"title": "Покупки",
"todos": [
{
"id": "a8f549bf-c73a-44b5-9209-43a9760f3d81",
"title": "Яйця",
"isCompleted": true,
"isFavorite": false
}
]
}
]
}
}
В нас є проблема: коли ми змінюємо нашу задачу з списку всіх задач, то ця що в списку задач групи "Покупки" не змінюється, і навпаки — коли ми змінюємо задачу в групі, задача в загальному списку залишається без змін, а це явно не те, чого б ми хотіли, так як в них id – ідентичні. З нашої точки зору це одна й та ж задача, проте для MST – це зовсім різні моделі, так як ми зробили її клон за допомогою clone(todo)
.