React -- (2) HOC 、Render Props 、hooks

111 阅读2分钟

这三者是目前 react 解决代码复用问题的主要方式:

一. HOC 高阶组件

HOC 自身不是React API,它是一种基于React 的组合特性而形成的设计模式。具体而言,高阶组件是参数为组件,返回值为新组件的函数。

栗如:现在有一个 CommentList 组件,它订阅外部资源,渲染评论列表!!

class CommentList extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {
      // 假设 "DataSource" 是个全局范围内的数据源变量
      comments: DataSource.getComments()
    };
  }

  componentDidMount() {
    // 订阅更改
    DataSource.addChangeListener(this.handleChange);
  }

  componentWillUnmount() {
    // 清除订阅
    DataSource.removeChangeListener(this.handleChange);
  }

  handleChange() {
    // 当数据源更新时,更新组件状态
    this.setState({
      comments: DataSource.getComments()
    });
  }

  render() {
    return (
      <div>
        {this.state.comments.map((comment) => (
          <Comment comment={comment} key={comment.id} />
        ))}
      </div>
    );
  }
}

稍后,编写了一个用于订阅单个博客帖子的组件,该帖子遵循类似的模式:

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 <TextBlock text={this.state.blogPost} />;
  }
}

会发现,这两个组件有异曲同工之妙,那我们就要思考,是不是可以将它们的共同逻辑抽象出来,然后实现代码复用!!!【此时就可以使用高阶组件,定义一个高阶组件函数,去分别包装它们两个,在高阶组件中实现资源的订阅,并直接将数据返回给它们!!!!

const CommentListWithSubscription = withSubscription(
  CommentList,
  (DataSource) => DataSource.getComments()
);

const BlogPostWithSubscription = withSubscription(
  BlogPost,
  (DataSource, props) => DataSource.getBlogPost(props.id)
);

优点:

  • 实现逻辑复用、不影响被包裹组件的内部逻辑。

缺点:

  • hoc传递给被包裹组件的props容易和被包裹后的组件原有的props重名,进而被覆盖

二. Render Props

Render Props 原理为:给组件传一个 props 属性 ---> render 【也可以是任意其他名】,该属性为一个函数,在里面定义一些组件需要独特渲染的内容。然后在组件定义的内部,去this.props.render()调用,从而进行渲染。

栗子:

2173.png

如上,为 antd 组件库,使用该方法进行封装的 < Table > 组件 ,将其作为属性传给组件,并在组件内部调用!!!

优点:

  • 数据共享、代码复用,将组件内的state作为props传递给调用者,将渲染逻辑交给调用者。

缺点:

  • 嵌套写法不够优雅

三. hooks

hooks 是React 16.8的新增特性。通过将可复用的代码抽象出来定义成一个hook函数,从而复用代码逻辑。

2174.png

如上,将订阅外部资源的代码抽离出来,定义一个函数,然后在需要使用的组件内部直接调用!!!

优点:

  • 使用直观
  • 不会出现嵌套地域问题

缺点:

  • hook 只能在组件顶层使用,不能在分支语句中使用