让前端飞

React Tips: 依赖注入、全局包裹Context

2018-03-23  本文已影响350人  coolheadedY

依赖注入

props 传递

  • props 层层传递
  • 很多组件并不需要使用 props
  • 不推荐
// Title.jsx
export default function Title(props) {
  return <h1>{ props.title }</h1>;
}
// Header.jsx
import Title from './Title.jsx';
export default function Header() {
  return (
    <header>
      <Title />
    </header>
  );
}
// App.jsx
import Header from './Header.jsx';
class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { title: 'React Dependency Injection' };
  }
  render() {
    return <Header />;
  }
}

HOC 高阶组件

// title.jsx
import React from 'react'

export default function Title(props) {
  return <h1>{ props.title }</h1>
}
// inject.jsx
import React from 'react'
export default function inject(Component) {
  return class Injector extends React.Component {
    render() {
      const { title } = this.props
      return (
        <Component
          {...this.state}
          {...this.props}
          {...this.children}
          title={ title }
        />
      )
    }
  }
}
// header.jsx
import React from 'react'
import inject from './inject'
import Title from './title'

const title = 'React Dependency Injection'
const EnhancedTitle = inject(Title)

export default function Header() {
  return (
    <header>
      <EnhancedTitle title={title} />
    </header>
  )
}

新版 Context API

// title.jsx
import React from 'react'
import { InjectContext } from './inject'

export default class Title extends React.Component {
  render() {
    return (
      <InjectContext.Consumer>
        {context => (
          <div>
            {console.log(context)}
            <h1>{context.title}</h1>
          </div>
        )}
      </InjectContext.Consumer>
    )
  }
}
// inject.jsx
import React from 'react'
export const InjectContext = React.createContext({})
// header.jsx
import React from 'react'
import Title from './title'
export default class Header extends React.Component {
  render() {
    return <Title />
  }
}
// App.js
import React, { Component } from 'react';
import Header from './header'
import { InjectContext } from './inject'

class App extends Component {
  render() {
    return (
      <InjectContext.Provider value={{ title: 'React Dependency Injection' }}>
        <Header />
      </InjectContext.Provider>
    );
  }
}

export default App;

全局包裹Context

新版Context API 实现

相比于单纯的数据对象,将context包装成一个提供一些方法的对象会是更好的实践。因为这样能提供一些方法供我们操作context里面的数据。

// dependcies.js
export default {
  data: {},
  get(key) {
    return this.data[key];
  },
  register(key, value) {
    this.data[key] = value;
  }
}
// header.jsx
import React from 'react'
import Title from './title.jsx'

export default function Header() {
  return (
    <header>
      <Title />
    </header>
  )
}
// inject.js
import React from 'react'

export const InjectContext = React.createContext({})

创建dependcies后可以用dependencies.register 注册数据

// App.js
import React, { Component } from 'react';

import dependencies from './dependencies'
import Header from './header'
import { InjectContext } from './inject'
dependencies.register('title', 'context-react-patterns')

class App extends Component {
  render() {
    return (
      <InjectContext.Provider value={dependencies}>
        <Header />
      </InjectContext.Provider>
    )
  }
}

export default App;

然后在 Title 组件中直接从 Context 获取数据

import React from 'react'
import { InjectContext } from './inject'

export default class Title extends React.Component {
  render() {
    return (
      <InjectContext.Consumer>
        {context => (
          <div>
            <h1>{context.get('title')}</h1>
          </div>
        )}
      </InjectContext.Consumer>
    )
  }
}

高阶组件 HOC 实现

// dependencies.jsx
import React from 'react'

let dependencies = {}

export function register(key, dependency) {
  dependencies[key] = dependency
}

export function fetch(key) {
  if (dependencies.hasOwnProperty(key)) return dependencies[key]
  throw new Error(`"${ key } is not registered as dependency.`)
}

export function wire(Component, deps, mapper) {

  return class Injector extends React.Component {
    constructor(props) {
      super(props)
      this._resolvedDependencies = mapper(...deps.map(fetch))
    }

    render() {
      return (
        <Component
          {...this.state}
          {...this.props}
          {...this._resolvedDependencies} // {title: "react-patterns"}
        />
      )
    }
  }
}

在App 组件中使用register 注册数据

// App.js
import React, { Component } from 'react';

import Header from './header'
import { register } from './dependencies'
register('awesome-title', 'HOC-react-patterns')

class App extends Component {
  render() {
    return <Header />
  }
}

export default App;

// header.jsx
import React from 'react'
import Title from './title.jsx'

export default function Header() {
  return (
    <header>
      <Title />
    </header>
  )
}

在 Title 组件中通过 wire 注入数据

// title.jsx
import React from 'react'
import { wire } from './dependencies'

const Title = props => (<h1>{ props.title }</h1>)

export default wire(Title, ['awesome-title'], title => ({ title }))
上一篇下一篇

猜你喜欢

热点阅读