Post

2019-08-14 React HOC

最近在项目中,组件中嵌套层级太深,传参太麻烦。在思考有没有更简单的办法。想到React中有一个HOC函数(high order components)。
react最近也release了一个新属性 hooks新属性
希望想到的两个点有帮助

1.先看看HOC

第一步,当然是去官方文档查看相关资料

官方文章介绍

medium上的一个篇文章

1
2
3
4
1.1为什么会有HOC
    使用hoc的目的是高效的重复利用组件
2.2什么是HOC
    HOC就是传入一个组件,返回一个新组件

PS:官方文档的原话是take a component and return a new component

1
2
3
4
5
6
const EnhancedComponent = hoc(OriginalComponent);
import { withFunctions } from 'my-module';
class OriginalComponent extends React.Component {
   ...
}
export default withFunctions(OriginalComponent);
1
2
3
4
5
6
7
8
9
10
export function withFunctions(OriginalComponent) {
   return class extends React.Component {
      // make some enhancements
      ...
      render() {
         //return original component with additional props
         return <OriginalComponent {...this.props} />
      }
   }
}

你可以定义,引用其他方法一样,来使用HOC

1
2
3
4
5
6
7
// src/hoc/index.js
import React from 'react';
export function withFunctions(WrappedComponent) {
   return class extends React.Component {
      ...
   }
}
1
2
// src/components/OriginalComponent.js
import { withFunctions } from '../hoc';

在刚学习react,和在使用react的过程中,这篇文章至少看过两遍。都不是很懂。 如今react也写了快两年了,再次看,带着疑问,目的去看,比之前更清晰。

在平常的项目中,引入外面库的时候,经常使用HOC,最常用的是,connect 平常项目中的用法是,

1
export default connectmapStateToProps,mapDispatchToProps(ComponentApp)

第二步,动动小手,依据官方文档,写个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// App.js
import { DataSource } from './util';
import { CommentListWithSubscription } from './CommentList';
import { BlogPostWithSubscription } from './BlogPost';

class App extends Component {
  render() {
    return (
      <div>
        <CommentListWithSubscription />
        <BlogPostWithSubscription />
      </div>

    );
  }
}
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// BlogPost.js
import React from 'react';
import { DataSource } from './util';
import { withSubscription } from './HOC';
import { List } from 'antd';

class BlogPost extends React.Component {
    constructor(props) {
      super(props);
      this.handleChange = this.handleChange.bind(this);
      this.state = {
        blogPost: DataSource.getBlogPost(props.id)
      };
    }
  
    componentDidMount() {
      DataSource.addChangeListener(this.handleChange);
    }
  
    componentWillUnmount() {
      DataSource.removeChangeListener(this.handleChange);
    }
  
    handleChange() {
      this.setState({
        blogPost: DataSource.getBlogPost(this.props.id)
      });
    }
  
    render() {
      return <List
        header={<div>blogPost</div>}
        footer={<div>blogPost</div>}
        bordered
        dataSource={this.state.blogPost}
        renderItem={blogPost => (
          <List.Item>
            <div key={blogPost.id}>{blogPost.content}</div>
          </List.Item>
        )}
      />
      ;
    }
  }

  export const BlogPostWithSubscription = withSubscription(
    BlogPost,
    (DataSource, props) => DataSource.getBlogPost(props.id)
  );
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// CommentList.js
import React from 'react';
import { DataSource } from './util';
import { withSubscription } from './HOC';
import { List } from 'antd';

class CommentList extends React.Component {
    constructor(props) {
      super(props);
      this.handleChange = this.handleChange.bind(this);
      this.state = {
        // "DataSource" is some global data source
        comments: DataSource.getComments()
      };
    }
  
    componentDidMount() {
      // Subscribe to changes
      DataSource.addChangeListener(this.handleChange);
    }
  
    componentWillUnmount() {
      // Clean up listener
      DataSource.removeChangeListener(this.handleChange);
    }
  
    handleChange() {
      // Update component state whenever the data source changes
      this.setState({
        comments: DataSource.getComments()
      });
    }
  
    render() {
      return (
        <List
          header={<div>Comment</div>}
          footer={<div>Comment</div>}
          bordered
          dataSource={this.state.comments}
          renderItem={comment => (
            <List.Item>
              <div key={comment.id}>{comment.content}</div>
            </List.Item>
          )}
        />
      );
    }
  }

  export const CommentListWithSubscription = withSubscription(
    CommentList,
    (DataSource) => DataSource.getComments()
  );
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
// util.js
export const DataSource = {
    getComments: () => {
        return [
            {id: 1, content: 'prefilerate'},
            {id: 2, content: 'lead to'},
            {id: 3, content: 'be responsible for'},
            {id: 4, content: 'contribute to'},
            {id: 5, content: 'enormous'},
            {id: 6, content: 'notation'}
        ]
    },
    getBlogPost: (id) => {
      return [
          {id: 1, content: 'prefilerate'},
          {id: 2, content: 'lead to'},
          {id: 3, content: 'be responsible for'},
          {id: 4, content: 'contribute to'},
          {id: 5, content: 'enormous'},
          {id: 6, content: 'notation'}
      ]
    },
    addChangeListener: (func) => {
      func()
    },
    removeChangeListener: (func) => {
      func()
    },
}

以上,还需要在项目中多实践,应用,思考。

This post is licensed under CC BY 4.0 by the author.