依赖注入

在React中,想做依赖注入(Dependency Injection)其实相当简单。请看下面这个例子:

  1. // Title.jsx
  2. export default function Title(props) {
  3. return <h1>{ props.title }</h1>;
  4. }
  1. // Header.jsx
  2. import Title from './Title.jsx';
  3. export default function Header() {
  4. return (
  5. <header>
  6. <Title />
  7. </header>
  8. );
  9. }
  1. // App.jsx
  2. import Header from './Header.jsx';
  3. class App extends React.Component {
  4. constructor(props) {
  5. super(props);
  6. this.state = { title: 'React Dependency Injection' };
  7. }
  8. render() {
  9. return <Header />;
  10. }
  11. }

试想,我们想把”React Dependency Injection”这个字符串传到我们的Title组件里面去。比较直观的方法是把这个字符串作为props传入Header组件,然后Header组件将这个字符串作为props传入Title组件。
在只有三个组件相互嵌套的情况下,上面的这种解决方式似乎能满足我们的需要。但是设想一下,随着组件数量的增加以及组件嵌套层次的加深,许多的组件都需要接收该组件本身并不需要关心的props,然后且简单地传递给子组件。直观上来看,上面的这种方法在这种情况下,似乎就不太适用了。

不过不用担心,事实上我们还有很多方法能实现依赖的注入,其中之一就是高阶组件(high-order component)。

  1. // inject.jsx
  2. var title = 'React Dependency Injection';
  3. export default function inject(Component) {
  4. return class Injector extends React.Component {
  5. render() {
  6. return (
  7. <Component
  8. {...this.state}
  9. {...this.props}
  10. title={ title }
  11. />
  12. )
  13. }
  14. };
  15. }
  1. // Title.jsx
  2. export default function Title(props) {
  3. return <h1>{ props.title }</h1>;
  4. }
  1. // Header.jsx
  2. import inject from './inject.jsx';
  3. import Title from './Title.jsx';
  4. var EnhancedTitle = inject(Title);
  5. export default function Header() {
  6. return (
  7. <header>
  8. <EnhancedTitle />
  9. </header>
  10. );
  11. }

在上面这种实现中,title这个字符串作为我们的数据没有被注入到整个的组件树中。相反,我们的数据只传递给了需要关注这个数据的组件。
这种实现看上去比上面优雅了很多,但事实上并没有完全解决我们的问题。
我们将title这个字符串通过inject这个高阶组件注入了进去,但是如果我的title字符串只能在我的Header组件这个层级获得呢?那么是不是意味着我还需要再从Header组件传递给inject组件呢?

这是一个实际可能会发生的问题,为了解决这个问题,接下来需要介绍一下React的Context API。
在React框架中,存在着context这样一个概念。context有点类似与事件总线(Event Bus),是一个无论在哪都可以访问的对象。不同的是,但是在context里面维持的全部都是我们的数据而非事件。context贯穿于整个组件树中,所有的组件都可以访问context。

使用方法

  1. var context = { title: 'React in patterns' };
  2. class App extends React.Component {
  3. getChildContext() {
  4. return context;
  5. }
  6. // ...
  7. }
  8. App.childContextTypes = {
  9. title: PropTypes.string
  10. };

A place where we need data

  1. class Inject extends React.Component {
  2. render() {
  3. var title = this.context.title;
  4. // ...
  5. }
  6. }
  7. Inject.contextTypes = {
  8. title: PropTypes.string
  9. };

需要注意的是,在最新的React官方文档中,Context已经不太被官方推荐使用了。Why Not To Use Context

参考资料: