09.05.2025, Ссылка на статью
В мире разработки на React’е мы очень часто пишем компоненты, которые тесно связаны, но потом обнаруживаем, что такие компоненты тяжело тестировать, поддерживать и изменять.
Статья предлагает рассматривать инверсию зависимостей, чтобы избавиться от таких проблем, как:
- тесная связь кода с API-слоём;
- сложность тестирования вызовов API;
- трудности в изменении источника данных;
- сложности с пониманием состояния запросов и т.д.
Для использования принципа инверсии зависимостей необходимо убедится в том, что модули разного уровня должны зависеть от интерфейсов, а не конкретных реализаций:
interface UserRepository {
getUser: () => Promise<User>;
}
class ApiUserRepository implements UserRepository {
async getUser(): Promise<User> {
const response = await fetch('/api/user');
return response.json();
}
}
class MockUserRepository implements UserRepository {
private userPromiseWithResolvers?: PromiseWithResolvers<User>;
getUser(): Promise<User> {
this.userPromiseWithResolvers = Promise.withResolvers();
return this.userPromiseWithResolvers.promise;
}
// Метод для резолва промиса
resolveWithUser(user: User) {
this.userPromiseWithResolvers?.resolve(user);
}
// Метод для реджекта промиса
rejectUser(error: Error) {
this.userPromiseWithResolvers?.reject(error);
}
}
Классы, которые имплементируют интерфейс для работы с API-слоём очень легко использовать, тестировать, поддерживать и изменять, т.к. компоненты будут принимать в пропах экземпляры таких интерфейсов.
Если придерживаться функционального подхода, то можно создать хуки, которые внутри себя уже должны работать с интерфейсами абстракций, что реализовать на практике намного сложнее, чем классовый подход.
Из статьи можно вынести лучшие практики:
- необходимо определять четкие интерфейсы, представляющие зависимости в проекте;
- использовать экземпляры интерфейсов через пропы или контекст (или через тот самый DI – TSyringe);
- писать легко тестируемые компоненты с изолированными тестами.