brickspert / blog

个人技术博客,博文写在 Issues 里。
4.1k stars 546 forks source link

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

Open brickspert opened 7 years ago

brickspert commented 7 years ago

助你完全理解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 commented 6 years ago

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

yanlele commented 6 years ago

写的非常的好

catgu commented 6 years ago

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

winnieping commented 6 years ago

好棒,我也来看啦

shesio commented 6 years ago

受教了

KayneWang commented 6 years ago

清晰易懂,感谢分享

jiangxtx commented 6 years ago

讲解通俗易懂,满分!

LoveofRedMoon commented 6 years ago

写的好棒~瞬间理解了

hu382337381 commented 6 years ago

不错,通俗易懂

ly678910 commented 6 years ago

讲的很赞

wangqiao66 commented 6 years ago

666

wangqiao66 commented 6 years ago

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

wangqiao66 commented 6 years ago

记得加我啊

xiaoape commented 6 years ago

感谢

terranc commented 6 years ago

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

LannyCodes commented 6 years ago

挺好的文章!

leeseean commented 5 years ago

厉害

wanCheng7 commented 5 years ago

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

wangshuang0 commented 5 years ago

必须赞,写的太好了

yelin2016 commented 5 years ago

666,简单明了,太棒了

MrLeihe commented 5 years ago

很棒

923427357 commented 5 years ago

清晰明了,赞~

EdisonFan commented 5 years ago

为什么函数里还要套上一层函数? 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 commented 5 years ago

通熟易懂

z-9527 commented 5 years ago

为什么函数里还要套上一层函数? 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 commented 5 years ago

仔细看,函数里套上一层函数 是为了将内部方法封装并返回。你可以在外部拿到newFunc ,它在什么时候执行有你来决定。 比如 let foo = wrapWithUsername(wrappedFunc) ,foo拿到了 newFunc ,foo什么时候执行有你决定。 下边不嵌套的方式 wrapWithUsername(wrappedFunc) 会直接执行newFunc 内部的方法。

------------------ 原始邮件 ------------------ 发件人: "张致豪"notifications@github.com; 发送时间: 2019年9月29日(星期天) 中午11:05 收件人: "brickspert/blog"blog@noreply.github.com; 抄送: "Subscribed"subscribed@noreply.github.com; 主题: Re: [brickspert/blog] 助你完全理解React高阶组件(Higher-Order Components) (#2)

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

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

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or mute the thread.

sunlili810 commented 4 years ago

直白易懂

ShuhaoBai commented 4 years ago

醍醐灌顶

cll123456 commented 3 years ago

666

liancheng-zcy commented 3 years ago

不错不错

Shurangan commented 2 years ago

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

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

jiejie-coding commented 5 months ago

讲的太好了= =