React高阶组件(译)

本文作者:IMWeb zzbozheng 原文出处:IMWeb社区 未经同意,禁止转载

原文:https://daveceddia.com/extract-state-with-higher-order-components/

高阶组件是对React代码进行更高层次重构的好方法,如果你想精简你的state和生命周期方法,那么高阶组件可以帮助你提取出可重用的函数。

什么是高阶组件?名字来源于高阶函数,一个函数可以接收另一个函数作为参数,并且有可能在执行后返回一个函数,这种函数就称之为高阶函数。你可能使用过高阶函数但是并没有真正意识到,例如Array.forEachArray.mapsetTimeout这些都是高阶函数,我们都知道这些函数全都是接受一个函数作为参数,当新的函数返回时,他已经发生了变化。

// Ok :)
setTimeout(function() {
  // do a thing after 500ms
}, 500);

// Sure...
[1, 2, 3].map(function(i) {
  // multiply each element by 2
  return i * 2;
});

// Wait what?
function middleware(store) {
  return function(next) {
    return function(action) {
      // do the thing
    }
  }
}

那么,什么又是高阶组件呢?其实就是把一个组件接收一个组件作为参数,并返回包裹后的组件。既然可以把另一个组件作为参数,那意味着他必须是一个函数。

我们先来看一个典型的高阶组件:

// It's a function...
function myHOC() {
  // Which returns a function that takes a component...
  return function(WrappedComponent) {
    // It creates a new wrapper component...
    class TheHOC extends React.Component {
      render() {
        // And it renders the component it was given
        return <WrappedComponent {...this.props} />;
      }
    }

    // Remember: it takes a component and returns a new component
    // Gotta return it here.
    return TheHOC;
  }
}

提取共享的state

如果有两个组件都需要加载同样的数据,那么他们会有相同的 componentDidMount 函数。

//BookDetails.js

import React, { Component } from 'react';
import * as API from '../api';  // let's just pretend this exists

class BookDetails extends Component {
  constructor(props) {
    super(props);
    this.state = {
      book: null
    };
  }

  componentDidMount() {
    API.getBook(this.props.bookId).then(book => {
      this.setState({ book });
    })
  }

  render() {
    const { book } = this.state;

    if(!book) {
      return <div>Loading...</div>;
    }

    return (
      <div>
        <img src={book.coverImg}/>
        <div>{book.author}</div>
        <div>{book.title}</div>
      </div>
    );
  }
}

export default BookDetails;
// BookSummary.js

import React, { Component } from 'react';
import * as API from '../api';  // let's just pretend this exists

class BookSummary extends Component {
  constructor(props) {
    super(props);
    this.state = {
      book: null
    };
  }

  componentDidMount() {
    API.getBook(this.props.bookId).then(book => {
      this.setState({ book });
    })
  }

  render() {
    const { book } = this.state;

    if(!book) {
      return <div>Loading...</div>;
    }

    return (
      <div>
        <div>{book.summary}</div>
      </div>
    );
  }
}

export default BookSummary;

1.找出重复的代码

每个组件中constructorcomponentDidMount都干着同样的事情,另外,在数据拉取时都会显示Loading... 文案,那么我们应该思考如何使用高阶组件来提取这些方法。

2.迁移重复的代码到高阶组件

// BookLoader.js

import * as API from 'api'; // let's just pretend this exists

// It's a function...
function loadBook() {
  // Which returns a function that takes a component...
  return function(WrappedComponent) {
    // It creates a new wrapper component...
    class BookLoader extends React.Component {
      // Here's the duplicated code from above:
      constructor(props) {
        super(props);
        this.state = {
          book: null
        };
      }

      componentDidMount() {
        API.getBook(this.props.bookId).then(book => {
          this.setState({ book });
        })
      }

      render() {
        const { book } = this.state;

        if(!book) {
          return <div>Loading...</div>;
        }

        // Notice how "book" is passed as a prop now 
        return (
          <WrappedComponent
            {...this.props}
            book={book} />
        );
      }
    }

    // Remember: it takes a component and returns a new component
    // Gotta return it here.
    return BookLoader;
  }
}

export default loadBook;

在 BookLoader 高阶组件中处理 book state,并且作为prop传递给已包裹的组件,使用相同的办法来处理Loading state,我们需要做的是拉取state,并且更新到组件中去。

3.包裹组件,并且使用props替换state

BookDetailsBookSummary组件应用 到新的BookLoader高阶组件中去:

// BookDetails.js

import React, { Component } from 'react';
import loadBook from './BookLoader';

class BookDetails extends Component {
  render() {
    // Now "book" comes from props instead of state
    const { book } = this.props;

    return (
      <div>
        <img src={book.coverImg}/>
        <div>{book.author}</div>
        <div>{book.title}</div>
      </div>
    );
  }
}

export default loadBook()(BookDetails);
// BookSummary.js

import React, { Component } from 'react';
import loadBook from './BookLoader';

class BookSummary extends Component {
  render() {
    // Now "book" comes from props instead of state
    const { book } = this.props;

    return (
      <div>
        <div>{book.summary}</div>
      </div>
    );
  }
}

export default loadBook()(BookSummary);

4.尽可能地简化

在你完成高阶组件迁移后,应该更进一步地简化你的代码,这个例子的组件比较简单,他们可以变成普通的功能:

// BookDetails.js

import loadBook from './BookLoader';

function BookDetails({ book }) {
  return (
    <div>
      <img src={book.coverImg}/>
      <div>{book.author}</div>
      <div>{book.title}</div>
    </div>
  );
}

export default loadBook()(BookDetails);
// BookSummary.js

import loadBook from './BookLoader';

function BookSummary({ book }) {
  return (
    <div>
      <div>{book.summary}</div>
    </div>
  );
}

export default loadBook()(BookSummary);

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Fundebug

​ECMAScript正则表达式6个最新特性

为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,翻译仅用于学习。

14740
来自专栏纯洁的微笑

springboot(四):thymeleaf使用详解

在上篇文章springboot(二):web综合开发中简单介绍了一下thymeleaf,这篇文章将更加全面详细的介绍thymeleaf的使用。thymeleaf...

832100
来自专栏飞雪无情的博客

Go语言实战笔记(二十六)| Go unsafe 包之内存布局

unsafe,顾名思义,是不安全的,Go定义这个包名也是这个意思,让我们尽可能的不要使用它,如果你使用它,看到了这个名字,也会想到尽可能的不要使用它,或者更小心...

8720
来自专栏互联网杂技

h5新功能data-*,好好利用,还能做数据双向绑定

标题是data-,但是这里主要讲伪元素,看明白就知道了 1、jQuery Selectors 获取伪元素的属性值 虽然JS里没有可以直接操作伪元素的选择符,然而...

32840
来自专栏狮乐园

async-for-js

插入3个div元素,其中第二个div元素使用setTimeout模拟异步操作,理想的插入顺序为div1 div2 div3,但这里的代码的插入顺序为div1 d...

14320
来自专栏Micro_awake web

HTML&CSS书写规范

第一部分:HTML书写规范: 1.1 HTML整体结构: 1.1.1:HTML基础设施: 文档以"<!DOCTYPE...>"首行顶格开始,推荐使用"<!DOC...

254100
来自专栏Coding迪斯尼

使用组件的state机制实现屏幕取词

12420
来自专栏Java3y

Juqery就是这么简单

什么是Jquery? Jquey就是一款跨主流浏览器的JavaScript库,简化JavaScript对HTML操作 就是封装了JavaScript,能够简化我...

34750
来自专栏数据结构与算法

2833 奇怪的梦境

题目描述 Description Aiden陷入了一个奇怪的梦境:他被困在一个小房子中,墙上有很多按钮,还有一个屏幕,上面显示了一些信息。屏幕上说,要将所有按钮...

38150
来自专栏与神兽党一起成长

手工实现表单重置的部分功能

首先我必须说几乎所有的人都不需要自己实现表单重置的功能,表单重置功能只需要一个reset类型的input就足够了。<input type="reset" />

18630

扫码关注云+社区

领取腾讯云代金券