【发布时间】:2015-05-18 19:09:50
【问题描述】:
在 Angular.js 中可以使用依赖注入。我做了一些浏览,但找不到它的实现。 React 有类似的东西吗?
【问题讨论】:
标签: angularjs dependency-injection reactjs
在 Angular.js 中可以使用依赖注入。我做了一些浏览,但找不到它的实现。 React 有类似的东西吗?
【问题讨论】:
标签: angularjs dependency-injection reactjs
React 有 IoC,但没有像 Angular 这样的 DI 容器的概念。也就是说,不是让容器知道如何创建对象并传递依赖项,而是在实例化组件时通过将 props 传递给组件来显式传递它们(如 <MyComponent items={this.state.items} />)。
不过,将依赖项作为 props 传递在 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 版本中更改,因此您可能希望稀疏地使用它,直到它被记录。
【讨论】:
React 组件中依赖注入的大部分解决方案都是基于上下文的。我认为很高兴知道引擎盖下发生了什么。在撰写本文时,最流行的构建 React 应用程序的方法之一就是 Redux。著名的连接函数和那里的 Provider 使用上下文。
从反应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>
);
}
}
使用像 InversifyJS 这样的 IoC 容器的主要优点是我们不使用上下文!
您可以通过here了解更多信息。
【讨论】:
我不太喜欢使用 contexts,因为它仍然是 react 的一个实验性功能,而且有点笨重。我也看过像 react-di 这样的 DI 框架,但它要求每个组件都知道 DI 框架注入依赖项的方式(即知道依赖项在 this.props.di 对象中。)。
如果我们排除上下文,将某些东西注入 React 组件的规范方法是通过使用 props。当你运行React.createElement 时,props 被注入,即对于每个 jsx 标签。 React.createElement 函数接受一个组件、一些道具和一些孩子,并返回一个React element。 IE。 (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} />;
}
小提琴:https://jsfiddle.net/8971g8s5/3/
这样做的好处是PropTypes 仍然可以很好地工作,因此每个组件都可以清楚地声明它想要什么样的属性。
另外,注入的接收端不需要依赖任何特殊的实现,只需要 React 的普通 props 系统。所以组件不需要知道你正在使用依赖注入或者你是怎么做的,他们只关心他们收到了什么道具。
【讨论】: