Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
brickspert opened this issue Sep 9, 2017 · 32 comments
Open
Labels

Comments

@brickspert
Copy link
Owner

brickspert commented Sep 9, 2017

助你完全理解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

@liujiusheng
Copy link

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

@yanlele
Copy link

yanlele commented Jan 26, 2018

写的非常的好

@catgu
Copy link

catgu commented Jan 29, 2018

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

@winnieping
Copy link

好棒,我也来看啦

@shesio
Copy link

shesio commented Feb 22, 2018

受教了

@KayneWang
Copy link

清晰易懂,感谢分享

@jiangxtx
Copy link

讲解通俗易懂,满分!

@LoveofRedMoon
Copy link

写的好棒~瞬间理解了

@hu382337381
Copy link

不错,通俗易懂

@ly678910
Copy link

ly678910 commented Jul 2, 2018

讲的很赞

@wangqiao66
Copy link

666

@wangqiao66
Copy link

写的太好了,你这么大神我们加下QQ号吧,日后可以更好的交流3154329581

@wangqiao66
Copy link

记得加我啊

@xiaoape
Copy link

xiaoape commented Jul 26, 2018

感谢

@terranc
Copy link

terranc commented Aug 27, 2018

喜欢这种不装B的直白描述

@LannyCodes
Copy link

挺好的文章!

@leeseean
Copy link

厉害

@wanCheng7
Copy link

前面用函数做例子后面再引入组件很棒,没有那么多虚头巴脑的名词,浅显易懂,赞!

@wangshuang0
Copy link

必须赞,写的太好了

@yelin2016
Copy link

666,简单明了,太棒了

@MrLeihe
Copy link

MrLeihe commented Jul 18, 2019

很棒

@923427357
Copy link

清晰明了,赞~

@EdisonFan
Copy link

为什么函数里还要套上一层函数?
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
Copy link

通熟易懂

@z-9527
Copy link

z-9527 commented Sep 29, 2019

为什么函数里还要套上一层函数?
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
Copy link

DaWeIDaDa commented Sep 29, 2019 via email

@sunlili810
Copy link

直白易懂

@ShuhaoBai
Copy link

醍醐灌顶

@cll123456
Copy link

666

@liancheng-zcy
Copy link

不错不错

@Shurangan
Copy link

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

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

@jiejie-coding
Copy link

讲的太好了= =

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests