Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Глубокие знания MobX
MobX - это библиотека для управления состоянием приложения на основе реактивного программирования. Я имею глубокие практические знания MobX и использовал его в production-приложениях. Поделюсь ключевыми концепциями и примерами.
Основные концепции MobX
1. Observable (Наблюдаемое состояние)
Observable - это объекты, которые отслеживаются MobX для изменений. MobX автоматически отслеживает, когда observable изменяется.
import { makeObservable, observable, action } from 'mobx';
class UserStore {
// Явное объявление observable
users: User[] = [];
isLoading = false;
error: string | null = null;
constructor() {
makeObservable(this, {
users: observable,
isLoading: observable,
error: observable,
setLoading: action,
setUsers: action,
setError: action,
});
}
setLoading(value: boolean) {
this.isLoading = value;
}
setUsers(users: User[]) {
this.users = users;
}
setError(error: string | null) {
this.error = error;
}
}
Alternative способ с декораторами (если используешь TypeScript):
class UserStore {
@observable users: User[] = [];
@observable isLoading = false;
@observable error: string | null = null;
@action
setLoading(value: boolean) {
this.isLoading = value;
}
}
2. Computed (Вычисляемые значения)
Computed создают значения, которые автоматически пересчитываются при изменении зависимостей. Они кэшируются и пересчитываются только при необходимости.
class UserStore {
@observable users: User[] = [];
@observable filter = '';
// Computed свойство
@computed
get filteredUsers(): User[] {
console.log('Filtering users...');
return this.users.filter(user =>
user.name.toLowerCase().includes(this.filter.toLowerCase())
);
}
// Или с makeObservable
constructor() {
makeObservable(this, {
users: observable,
filter: observable,
filteredUsers: computed,
activeUsers: computed,
setFilter: action,
});
}
get activeUsers(): User[] {
return this.users.filter(u => u.active);
}
}
// Использование
const store = new UserStore();
store.filter = 'john';
console.log(store.filteredUsers); // Computed выполнится один раз
console.log(store.filteredUsers); // Возьмется кэшированное значение
store.filter = 'jane';
console.log(store.filteredUsers); // Пересчитается, т.к. filter изменился
3. Actions (Действия для изменения состояния)
Actions - это функции, которые изменяют observable. MobX отслеживает все изменения внутри actions и запускает observers только один раз после завершения action.
class UserStore {
@observable users: User[] = [];
@observable isLoading = false;
@action
async loadUsers() {
this.isLoading = true;
try {
const response = await fetch('/api/users');
const data = await response.json();
this.users = data; // Одно изменение для observer
this.isLoading = false; // Второе изменение
// Observer запустится один раз, после завершения action
} catch (error) {
this.isLoading = false;
}
}
// Асинхронный action
@action
async updateUser(id: string, data: Partial<User>) {
const user = this.users.find(u => u.id === id);
if (user) {
Object.assign(user, data);
}
}
}
4. Reactions (Реакции)
Reactions позволяют выполнять side effects при изменении observable.
import { reaction, autorun } from 'mobx';
class UserStore {
@observable searchQuery = '';
@observable results: User[] = [];
constructor() {
// autorun - выполняется сразу и при изменении зависимостей
autorun(() => {
console.log('Search query:', this.searchQuery);
});
// reaction - более тонкий контроль
reaction(
() => this.searchQuery,
(query) => {
// Выполнится только при изменении searchQuery
this.performSearch(query);
},
{
delay: 500 // Debounce
}
);
}
@action
async performSearch(query: string) {
const data = await fetch(`/api/search?q=${query}`).then(r => r.json());
this.results = data;
}
}
Использование с React
5. Observer компонент
import { observer } from 'mobx-react-lite';
import { createContext, useContext } from 'react';
const UserStoreContext = createContext<UserStore | null>(null);
const useUserStore = () => {
const store = useContext(UserStoreContext);
if (!store) throw new Error('Store not provided');
return store;
};
// Observer компонент автоматически подписывается на изменения
const UserList = observer(() => {
const store = useUserStore();
return (
<div>
<input
value={store.filter}
onChange={(e) => store.setFilter(e.target.value)}
placeholder="Filter users"
/>
{store.isLoading && <p>Loading...</p>}
<ul>
{store.filteredUsers.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
<p>Found: {store.filteredUsers.length}</p>
</div>
);
});
const UserStats = observer(() => {
const store = useUserStore();
return <div>Total users: {store.users.length}</div>;
});
Продвинутые техники
6. Логирование изменений
import { spy } from 'mobx';
spy((event) => {
if (event.type === 'action') {
console.log(`Action: ${event.name}`);
}
if (event.type === 'reaction') {
console.log('Reaction triggered');
}
});
7. Flow для асинхронности
import { flow } from 'mobx';
class UserStore {
@observable user: User | null = null;
@observable isLoading = false;
// flow - лучше чем обычный async/await
loadUser = flow(function* (id: string) {
this.isLoading = true;
try {
const response: User = yield fetch(`/api/users/${id}`).then(r => r.json());
this.user = response;
} finally {
this.isLoading = false;
}
});
}
8. Runnable examples
class TodoStore {
@observable todos: Todo[] = [];
@observable filter: 'all' | 'completed' | 'pending' = 'all';
@computed
get filteredTodos(): Todo[] {
switch (this.filter) {
case 'completed':
return this.todos.filter(t => t.completed);
case 'pending':
return this.todos.filter(t => !t.completed);
default:
return this.todos;
}
}
@computed
get stats() {
return {
total: this.todos.length,
completed: this.todos.filter(t => t.completed).length,
pending: this.todos.filter(t => !t.completed).length,
};
}
@action
addTodo(text: string) {
this.todos.push({
id: Math.random(),
text,
completed: false,
});
}
@action
toggleTodo(id: number) {
const todo = this.todos.find(t => t.id === id);
if (todo) {
todo.completed = !todo.completed;
}
}
}
Когда использовать MobX
Плюсы:
- Минимальный boilerplate кода
- Автоматическое отслеживание зависимостей
- Отличная производительность благодаря кэшированию computed
- Легко читать и понимать код
Минусы:
- Может быть сложнее отследить, где изменилось состояние
- Требует большей дисциплины разработчиков
- Не так популярен как Redux в больших командах
MobX отлично подходит для проектов среднего и большого размера, где важна разработчикам нужна высокая производительность и чистый код.