Подтвердить что ты не робот

Реагировать на инъекцию зависимостей или подобное?

В Angular.js можно использовать инъекцию зависимостей. Я сделал некоторые просмотры и не смог найти его реализацию. Имеет ли React что-то подобное этому?

4b9b3361

Ответ 1

Реакция имеет IoC, но не существует какой-либо концепции контейнера DI, такого как Angular. То есть вместо того, чтобы иметь контейнер, который знает, как создавать объекты и передавать в зависимостях, вы передаете их явно, передавая реквизиты компоненту при его создании (например, <MyComponent items={this.state.items} />).

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

React имеет концепцию context, которая является общим объектом для всего дерева компонентов. Таким образом, компонент верхнего уровня может сказать, что контекст для его поддерева имеет объект, содержащий нечто вроде UserStore, MessageStore и т.д. Компонент, расположенный далее в иерархии компонентов, может затем сказать, что он хочет получить доступ к UserStore в своем контексте. Говоря это, UserStore доступен для этого компонента без необходимости явно передавать его из верхнего компонента в нижнюю часть, а запрашивающий его компонент не знает, как он был создан/передан ему.

Он имеет преимущество контейнера DI, в котором у вас есть центральное место для создания объекта, которое может быть передано дальше. Здесь хорошее введение в контексты: https://www.tildedave.com/2014/11/15/introduction-to-contexts-in-react-js.html

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

Ответ 2

Мне не очень нравится использовать контексты, так как он все еще является экспериментальной особенностью реакции и своего рода громоздким. Я также рассмотрел структуры DI, такие как react-di, но потребовал, чтобы каждый компонент знал о карьере DI для инъекций зависимостей (т.е. зная, что зависимость находится в объекте this.props.di.).

Если мы исключаем контексты, канонический способ внедрить что-то в компонент React - это использование реквизита. Реквизиты вводятся при запуске React.createElement, то есть для каждого тега jsx. Функция React.createElement принимает компонент, некоторые реквизиты и некоторые дочерние элементы и возвращает React element. То есть (component, props, children) -> element.

Я сделал функцию createComponent с почти той же самой сигнатурой, что и React.createElement, но которая возвращает компонент, т.е. (component, props, children) -> component. Вот он:

const createComponent = (type, defaultProps = {}, defaultChildren = null) => {
    return ({ children, ...props }) => {
        return React.createElement(
            type,
            { ...defaultProps, ...props },
            children || defaultChildren
        );
    };
};

Возвращаемый компонент может быть введен в prop, как в этом примере:

const Banner = ({ children, TextComponent }) => {
    return <div className="banner">
        <TextComponent>{children}</TextComponent>
    </div>;
}

const SayHelloComponent = ({ ParagraphComponent }) => {
    return <ParagraphComponent>Hello world!</ParagraphComponent>;
}

const ParentComponent = () => {
    const inject = {
        ParagraphComponent: createComponent(Banner, {
            TextComponent: createComponent('span', {
                className: "my-pretty-class",
            }),
        }),
    }

    return <SayHelloComponent {...inject} />;
}

Fiddle: https://jsfiddle.net/8971g8s5/3/

Хорошо, что PropTypes будет работать очень хорошо, поэтому каждый компонент может четко заявить, какие свойства он хочет.

Кроме того, принимающий конец инъекции не должен зависеть от какой-либо специальной реализации, а именно от обычной системы реквизита React. Поэтому компоненты не должны знать, что вы используете инъекцию зависимостей или как вы это делаете, они просто заботятся о том, какие реквизиты они получают.

Ответ 3

Из react-in-patterns:

Большинство решений для инъекций зависимостей в компонентах React основаны на контексте. Я думаю, что хорошо знать, что происходит под капотом. Как время написания одного из самых популярных способов создания приложений React включает Redux. Знаменитая функция соединения и Провайдер там используют контекст.

И из реакции docs:

Контекст - это расширенная и экспериментальная функция. API, вероятно, изменится в будущих выпусках.

В большинстве приложений никогда не будет необходимости использовать контекст. Особенно, если вы только начинаете с React, вы, вероятно, не хотите использовать контекст. Использование контекста сделает ваш код более понятным, потому что он делает поток данных менее ясным. Это похоже на использование глобальных переменных для передачи состояния через ваше приложение.

Если вам нужно использовать контекст, используйте его экономно.

Независимо от того, создаете ли вы приложение или библиотеку, попытайтесь изолировать использование контекста до небольшой области и избегайте использования контекстного API, когда это возможно, чтобы было легче обновить его при изменении API.

Я нашел способ ввода зависимостей без использования контекста благодаря использованию контейнера IoC.

Большинство контейнеров поддерживают два вида инъекций:

  • Инъекция конструктора. Чтобы использовать "инъекцию конструктора", контейнер IoC должен иметь возможность создавать экземпляры классов. В React компоненты иногда являются просто функциями (а не классами), и мы не можем делегировать создание экземпляров компонентов в контейнер IoC. Это означает, что инъекция конструктора, работающая на контейнерах IoC, не играет хорошо с React.

  • Вложение свойств: Хорошо работает с React, если мы хотим передать зависимости компонентам, не передавая их явно через каждый компонент.

Я использую InversifyJS как контейнер IoC и его поддержку впрыска свойств для передачи зависимостей компонентам, не передавая их явно через каждый компонент и не используя контекст:

import { pInject } from "./utils/di";
import { UserStore } from "./store/user";

class User extends React.Component<any, any> {

    @pInject(UserStore)
    private userStore: UserStore; // INJECTED!

    public render() {
        return (
            <h1>{this.userStore.pageTitle}</h1>
        );
    }
}

Основным преимуществом использования контейнера IoC, такого как InversifyJS, является то, что мы не используем контекст!

Подробнее об этом можно узнать здесь.