依赖注入

props 传递

  • props 层层传递
  • 很多组件并不需要使用 props
  • 不推荐
1
2
3
4
// Title.jsx
export default function Title(props) {
return <h1>{ props.title }</h1>;
}
1
2
3
4
5
6
7
8
9
// Header.jsx
import Title from './Title.jsx';
export default function Header() {
return (
<header>
<Title />
</header>
);
}
1
2
3
4
5
6
7
8
9
10
11
// 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 高阶组件

1
2
3
4
5
6
// title.jsx
import React from 'react'

export default function Title(props) {
return <h1>{ props.title }</h1>
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 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 }
/>
)
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 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>
)
}
}
1
2
3
// inject.jsx
import React from 'react'
export const InjectContext = React.createContext({})
1
2
3
4
5
6
7
8
// header.jsx
import React from 'react'
import Title from './title'
export default class Header extends React.Component {
render() {
return <Title />
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 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里面的数据。

1
2
3
4
5
6
7
8
9
10
// dependcies.js
export default {
data: {},
get(key) {
return this.data[key];
},
register(key, value) {
this.data[key] = value;
}
}
1
2
3
4
5
6
7
8
9
10
11
// header.jsx
import React from 'react'
import Title from './title.jsx'

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

export const InjectContext = React.createContext({})

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 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 获取数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 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 注册数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 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;

1
2
3
4
5
6
7
8
9
10
11
// header.jsx
import React from 'react'
import Title from './title.jsx'

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

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

1
2
3
4
5
6
7
// 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 }))