Открыть список
Как стать автором
Обновить
19.2
Карма
0
Рейтинг
Павел Богатырёв @PFight77

Web-developer

Архитектурный паттерн Dependency Injection в React-приложении

Конструктор в обобщенном понимании — это тот интерфейс, через который осуществляется доступ к сущности. Именно так следует понимать слово "конструктор" в концепции "constructor injection", потому как смысл этой концепции — поместить зависимости там, где их невозможно проигнорировать, забыть, где о них подскажет компилятор. В ООП — это действительно конструктор класса (хотя не всегда, при использовании фабрик и других подобных паттернов объекты могут создаваться не через конструктор, и там тоже нужно подходить творчески к этому термину). В React роль конструктора выполняют props, так как не передав props мы не можем использовать компонент. Использование constructor injection в react должно подразумевать именно использование props.

Архитектурный паттерн Dependency Injection в React-приложении

С учетом diInject у Вас получился Service Locator на самом деле, а не Constructor Injection. Как бы на уровне необернутого компонента вроде как есть Constructor Injection, но Вы все компоненты оборачиваете, и обернутые они уже просто берут что найдут в контейнере, и никто этот процесс не контролирует.

Архитектурный паттерн Dependency Injection в React-приложении

Я так понимаю, что никакой проверки типа у второго параметра diInject нет? То есть, TypeScript не проверяет, что booksListModel: ListModel<IBook>; в объявлении Props и booksListModel: new Dependence(constants.booksListModelName), должны соответствовать друг другу, или хотя бы, что все зависимости переданы.

Архитектурный паттерн Dependency Injection в React-приложении

Увидел много кода с классами, и так и не увидел кода компонента React, который использует данный подход. В статье есть только такое:


Теперь перейдем к компоненту, который будет получать из нашего контейнера необходимые данные в виде пропсов.

И все, дальше сбивчиво текстом объясняется, но я так и не понял, каким образом компонент объявляет свои зависимости, и каким образом это объявление читает HOC. Можно привести код хотя бы одного такого компонента? Про использование Inversify с обычным классами можно почитать в документации Inversify, я надеялся увидеть то, что заявлено в заголовке статьи.

Напиши мне GraphQL сервер на C#

Не, пока не пробовали.

15 малоизвестных свойств и методов объектов DOM

Вот только он дергает страницу резко и не всегда по делу. Например, элемент может быть уже виден, а страница все-равно дернется так что пользователь потеряет контекст.
У него по спеке есть всякие няшные параметры (вроде плавной прокрутки), но их мало кто поддерживает.

Напиши мне GraphQL сервер на C#

Главное, чтобы в Вашем API были нормальные сигнатуры функций. То есть, возвращаемое значение — модель, а не ActionResult какой-нибудь. Тогда можно рефлекшеном пробежаться и сгенерировать схему как в статье.

Напиши мне GraphQL сервер на C#

Когда делаешь классы наследники ObjectGraphType подразумевается, что вся схема известна на этапе компиляции. Здесь я генерирую схему на основе динамических метаданных Docsvision.

Напиши мне GraphQL сервер на C#

Ух ты, спасибо, как-то не нашел. Там правда в январе этого года первый коммит, молодая библиотека.

Нативная инверсия зависимостей в TypeScript и React

Лучше путь в импортах + имя

В typedin у меня еще лучше — ключем выступает ссылка на конструктор класса. Само собой, с ограничением, что нельзя использовать интерфейсы.

Нативная инверсия зависимостей в TypeScript и React

Вы однобоко рассматриваете внедрение зависимостей только с точки зрения удобства тестирования. Есть еще повторное использование кода, причем возможно не автором кода. В этом юзкейсе только явные зависимости в интерфейсе и ничего другого быть не может.


Вот допустим, разместили вы на гитхабе компонент, и хотите сделать там инверсию зависимостей. Использование чего-то вроде inversify тут было бы странным, а вот описанный мной подход очень подходит.

Нативная инверсия зависимостей в TypeScript и React

Да, клонировать там будет правильнее.


Кстати как бороться с неуникальностью ключей в services?

Ключ в этой системе должен быть уникальным. Допустим, в Service Locator сервисы получаются по имени интерфейса — соответственно, имя интерфейса тоже должно быть уникальным. Согласен, что есть потенциальная проблема.


Экспериментируя с DI

А как потом использовать TodosView? Как передать ему другую реализацию сервиса? И где он создает для себя свои зависимости (где создается TodosRespository)?

Нативная инверсия зависимостей в TypeScript и React

Хм, как Вам такой вариант:


interface MyComponentProps {
    services: $Logger;
}

class MyComponent extends React.Component<MyComponentProps, {}> {
    defaultServices = {
        logger: new Logger();
    };
    services: $Logger;
    constructor(props) {
        this.services = applyDefaultServices(props.services, this.defaultServices);
    }
}

function applyDefaultServices(services, defaultServices) {
    for (let prop in defaultServices) {
        if (!services[prop]) {
            services[prop] = defaultServices[prop];
        }
    }
}

Выглядит вполне прилично. Еще там выше предлагали вариант, как можно объявлять services родительского компонента через services: $Logger & SomeChildComponentProps['services'].


генерацией метаданных из сигнатур конструкторов и выстраиванием на их основе DI

Можете подробнее? Пока я вижу в Ваших словах все тот же Service Locator. Если мы не передаем явно каждый раз зависимости, то значит компонент можно создать не передав ему нужные зависимости. В этом вся соль constructor injection — он не позволяет такой ситуации возникнуть на уровне компилятора.

Нативная инверсия зависимостей в TypeScript и React

Чуть выше уже ответил staticlab на аналогичный вопрос.

Нативная инверсия зависимостей в TypeScript и React

Первые 2 пункта в сторону constructor injection как такового. Если их решить, то получится Service Locator…


Все зависимости по-умолчанию жесткие.

У нас есть хитрая система, которая позволяет писать так:


services: $Logger = App.Instance;

Об этом будет статья чуть позже.


Как быть с зависимостями сервисов в compostion root?

Вот пример из статьи:


let logger = new Logger();
export var services = {
    logger: logger,
    localStorage: new LocalStorage(),
    heroService: new HeroService({ logger }) // Обратите внимание!
};

Нативная инверсия зависимостей в TypeScript и React

Тогда мы опять вернемся к Service Locator. Проблема с контекстом в том, что он не представлен в публичном интерфейсе компонента. То есть, пользователь может создать компонент, не передав ему необходимые сервисы, и узнает об ошибке только во время выполнения. При этом, выяснить какие компоненту нужны сервисы можно только через документацию, изучение его кода или методом проб и ошибок.

Нативная инверсия зависимостей в TypeScript и React

Не знал что так можно, спасибо. Отличное решение.

Нативная инверсия зависимостей в TypeScript и React

Да, в этом идея constructor injection. Передавать все сервисы по одному будет более накладно — суть моего подхода в том, чтобы свести эти накладные издержки синтаксиса к минимуму. Впрочем, там выше mayorovp предложил отличный вариант:


export interface AnotherComponentProps {
    services: $Logger & SomeComponentProps['services'];
}

Информация

В рейтинге
5,956-й
Откуда
Орел, Орловская обл., Россия
Зарегистрирован
Активность