Skip to content

助你完全理解React高阶组件(Higher-Order Components) #2

Open
@brickspert

Description

@brickspert
Owner

助你完全理解React高阶组件(Higher-Order Components)

有时候人们很喜欢造一些名字很吓人的名词,让人一听这个名词就觉得自己不可能学会,从而让人望而却步。但是其实这些名词背后所代表的东西其实很简单。来自React.js 小书

高阶组件定义

a higher-order component is a function that takes a component and returns a new component.

翻译:高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。

理解了吗?看了定义似懂非懂?继续往下看。

函数模拟高阶组件

我们通过普通函数来理解什么是高阶组件哦~

  1. 最普通的方法,一个welcome,一个goodbye。两个函数先从localStorage读取了username,然后对username做了一些处理。
function welcome() {
    let username = localStorage.getItem('username');
    console.log('welcome ' + username);
}

function goodbey() {
    let username = localStorage.getItem('username');
    console.log('goodbey ' + username);
}

welcome();
goodbey();
  1. 我们发现两个函数有一句代码是一样的,这叫冗余唉。不好不好~(你可以把那一句代码理解成平时的一大堆代码)

    我们要写一个中间函数,读取username,他来负责把username传递给两个函数。

function welcome(username) {
    console.log('welcome ' + username);
}

function goodbey(username) {
    console.log('goodbey ' + username);
}

function wrapWithUsername(wrappedFunc) {
    let newFunc = () => {
        let username = localStorage.getItem('username');
        wrappedFunc(username);
    };
    return newFunc;
}

welcome = wrapWithUsername(welcome);
goodbey = wrapWithUsername(goodbey);

welcome();
goodbey();

好了,我们里面的wrapWithUsername函数就是一个“高阶函数”。
他做了什么?他帮我们处理了username,传递给目标函数。我们调用最终的函数welcome的时候,根本不用关心username是怎么来的。

我们增加个用户study函数。

function study(username){
    console.log(username+' study');
}
study = wrapWithUsername(study);

study();

这里你是不是理解了为什么说wrapWithUsername是高阶函数?我们只需要知道,用wrapWithUsername包装我们的study函数后,study函数第一个参数是username

我们写平时写代码的时候,不用关心wrapWithUsername内部是如何实现的。

高阶组件

高阶组件就是一个没有副作用的纯函数。

我们把上一节的函数统统改成react组件。

  1. 最普通的组件哦。

welcome函数转为react组件。

import React, {Component} from 'react'

class Welcome extends Component {
    constructor(props) {
        super(props);
        this.state = {
            username: ''
        }
    }

    componentWillMount() {
        let username = localStorage.getItem('username');
        this.setState({
            username: username
        })
    }

    render() {
        return (
            <div>welcome {this.state.username}</div>
        )
    }
}

export default Welcome;

goodbey函数转为react组件。

import React, {Component} from 'react'

class Goodbye extends Component {
    constructor(props) {
        super(props);
        this.state = {
            username: ''
        }
    }

    componentWillMount() {
        let username = localStorage.getItem('username');
        this.setState({
            username: username
        })
    }

    render() {
        return (
            <div>goodbye {this.state.username}</div>
        )
    }
}

export default Goodbye;
  1. 现在你是不是更能看到问题所在了?两个组件大部分代码都是重复的唉。

按照上一节wrapWithUsername函数的思路,我们来写一个高阶组件(高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件)。

import React, {Component} from 'react'

export default (WrappedComponent) => {
    class NewComponent extends Component {
        constructor() {
            super();
            this.state = {
                username: ''
            }
        }

        componentWillMount() {
            let username = localStorage.getItem('username');
            this.setState({
                username: username
            })
        }

        render() {
            return <WrappedComponent username={this.state.username}/>
        }
    }

    return NewComponent
}

这样我们就能简化Welcome组件和Goodbye组件。

import React, {Component} from 'react';
import wrapWithUsername from 'wrapWithUsername';

class Welcome extends Component {

    render() {
        return (
            <div>welcome {this.props.username}</div>
        )
    }
}

Welcome = wrapWithUsername(Welcome);

export default Welcome;
import React, {Component} from 'react';
import wrapWithUsername from 'wrapWithUsername';

class Goodbye extends Component {

    render() {
        return (
            <div>goodbye {this.props.username}</div>
        )
    }
}

Goodbye = wrapWithUsername(Goodbye);

export default Goodbye;

看到没有,高阶组件就是把username通过props传递给目标组件了。目标组件只管从props里面拿来用就好了。

到这里位置,高阶组件就讲完了。你再返回去理解下定义,是不是豁然开朗~

你现在理解react-reduxconnect函数~

reduxstateaction创建函数,通过props注入给了Component
你在目标组件Component里面可以直接用this.props去调用redux stateaction创建函数了。

ConnectedComment = connect(mapStateToProps, mapDispatchToProps)(Component);

相当于这样

// connect是一个返回函数的函数(就是个高阶函数)
const enhance = connect(mapStateToProps, mapDispatchToProps);
// 返回的函数就是一个高阶组件,该高阶组件返回一个与Redux store
// 关联起来的新组件
const ConnectedComment = enhance(Component);

antd的Form也是一样的

const WrappedNormalLoginForm = Form.create()(NormalLoginForm);

参考地址:

  1. http://huziketang.com/books/react/lesson28
  2. https://react.bootcss.com/react/docs/higher-order-components.html

❤️感谢大家

关注公众号「前端技术砖家」,拉你进交流群,大家一起共同交流和进步。

image

Activity

liujiusheng

liujiusheng commented on Jan 25, 2018

@liujiusheng

666.写这么详细。我今天才重构了我的第一篇文章。

yanlele

yanlele commented on Jan 26, 2018

@yanlele

写的非常的好

catgu

catgu commented on Jan 29, 2018

@catgu

写的好棒!一看就明白了,感谢感谢

winnieping

winnieping commented on Feb 5, 2018

@winnieping

好棒,我也来看啦

shesio

shesio commented on Feb 22, 2018

@shesio

受教了

KayneWang

KayneWang commented on Feb 27, 2018

@KayneWang

清晰易懂,感谢分享

jiangxtx

jiangxtx commented on Mar 14, 2018

@jiangxtx

讲解通俗易懂,满分!

LoveofRedMoon

LoveofRedMoon commented on Mar 30, 2018

@LoveofRedMoon

写的好棒~瞬间理解了

hu382337381

hu382337381 commented on Apr 24, 2018

@hu382337381

不错,通俗易懂

ly678910

ly678910 commented on Jul 2, 2018

@ly678910

讲的很赞

wangqiao66

wangqiao66 commented on Jul 17, 2018

@wangqiao66

666

9 remaining items

yelin2016

yelin2016 commented on May 30, 2019

@yelin2016

666,简单明了,太棒了

MrLeihe

MrLeihe commented on Jul 18, 2019

@MrLeihe

很棒

923427357

923427357 commented on Aug 13, 2019

@923427357

清晰明了,赞~

EdisonFan

EdisonFan commented on Aug 29, 2019

@EdisonFan

为什么函数里还要套上一层函数?
function wrapWithUsername(wrappedFunc) {
let newFunc = () => {
let username = localStorage.getItem('username');
wrappedFunc(username);
};
return newFunc;
}
这样写不行吗?
function wrapWithUsername(wrappedFunc) {
let username = localStorage.getItem('username');
wrappedFunc(username);
}

NewPrototype

NewPrototype commented on Sep 3, 2019

@NewPrototype

通熟易懂

z-9527

z-9527 commented on Sep 29, 2019

@z-9527

为什么函数里还要套上一层函数?
function wrapWithUsername(wrappedFunc) {
let newFunc = () => {
let username = localStorage.getItem('username');
wrappedFunc(username);
};
return newFunc;
}
这样写不行吗?
function wrapWithUsername(wrappedFunc) {
let username = localStorage.getItem('username');
wrappedFunc(username);
}

不返回一个新的函数,外面怎么调用?

DaWeIDaDa

DaWeIDaDa commented on Sep 29, 2019

@DaWeIDaDa
sunlili810

sunlili810 commented on Oct 14, 2019

@sunlili810

直白易懂

ShuhaoBai

ShuhaoBai commented on Oct 7, 2020

@ShuhaoBai

醍醐灌顶

cll123456

cll123456 commented on Feb 24, 2021

@cll123456

666

liancheng-zcy

liancheng-zcy commented on Mar 31, 2021

@liancheng-zcy

不错不错

Shurangan

Shurangan commented on Apr 10, 2022

@Shurangan

函数 wrapWithUsername (wrappedFunc) { let newFunc = () => { let username = localStorage.getItem('username'); WrappedFunc(用户名); }; 返回新函数; } 这样写不行吗? function wrapWithUsername(wrappedFunc) { let username = localStorage.getItem('username'); WrappedFunc(用户名); }

您仔细看,返回的是一个回调函数,类似函数柯里化

jiejie-coding

jiejie-coding commented on Apr 29, 2024

@jiejie-coding

讲的太好了= =

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @terranc@liujiusheng@wangshuang0@hu382337381@jiangxtx

        Issue actions

          助你完全理解React高阶组件(Higher-Order Components) · Issue #2 · brickspert/blog