助你完全理解React高阶组件(Higher-Order Components)
brickspert opened this issue · 32 comments
助你完全理解React高阶组件(Higher-Order Components)
有时候人们很喜欢造一些名字很吓人的名词,让人一听这个名词就觉得自己不可能学会,从而让人望而却步。但是其实这些名词背后所代表的东西其实很简单。来自React.js 小书
高阶组件定义
a higher-order component is a function that takes a component and returns a new component.
翻译:高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。
理解了吗?看了定义似懂非懂?继续往下看。
函数模拟高阶组件
我们通过普通函数来理解什么是高阶组件哦~
- 最普通的方法,一个
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();
-
我们发现两个函数有一句代码是一样的,这叫冗余唉。不好不好~(你可以把那一句代码理解成平时的一大堆代码)
我们要写一个中间函数,读取
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
组件。
- 最普通的组件哦。
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;
- 现在你是不是更能看到问题所在了?两个组件大部分代码都是重复的唉。
按照上一节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-redux
的connect
函数~
把redux
的state
和action
创建函数,通过props
注入给了Component
。
你在目标组件Component
里面可以直接用this.props
去调用redux state
和action
创建函数了。
ConnectedComment = connect(mapStateToProps, mapDispatchToProps)(Component);
相当于这样
// connect是一个返回函数的函数(就是个高阶函数)
const enhance = connect(mapStateToProps, mapDispatchToProps);
// 返回的函数就是一个高阶组件,该高阶组件返回一个与Redux store
// 关联起来的新组件
const ConnectedComment = enhance(Component);
antd
的Form也是一样的
const WrappedNormalLoginForm = Form.create()(NormalLoginForm);
参考地址:
- http://huziketang.com/books/react/lesson28
- https://react.bootcss.com/react/docs/higher-order-components.html
❤️感谢大家
关注公众号「前端技术砖家」,拉你进交流群,大家一起共同交流和进步。
666.写这么详细。我今天才重构了我的第一篇文章。
写的非常的好
写的好棒!一看就明白了,感谢感谢
好棒,我也来看啦
受教了
清晰易懂,感谢分享
讲解通俗易懂,满分!
写的好棒~瞬间理解了
不错,通俗易懂
讲的很赞
666
写的太好了,你这么大神我们加下QQ号吧,日后可以更好的交流3154329581
记得加我啊
感谢
喜欢这种不装B的直白描述
挺好的文章!
厉害
前面用函数做例子后面再引入组件很棒,没有那么多虚头巴脑的名词,浅显易懂,赞!
必须赞,写的太好了
666,简单明了,太棒了
很棒
清晰明了,赞~
为什么函数里还要套上一层函数?
function wrapWithUsername(wrappedFunc) {
let newFunc = () => {
let username = localStorage.getItem('username');
wrappedFunc(username);
};
return newFunc;
}
这样写不行吗?
function wrapWithUsername(wrappedFunc) {
let username = localStorage.getItem('username');
wrappedFunc(username);
}
通熟易懂
为什么函数里还要套上一层函数?
function wrapWithUsername(wrappedFunc) {
let newFunc = () => {
let username = localStorage.getItem('username');
wrappedFunc(username);
};
return newFunc;
}
这样写不行吗?
function wrapWithUsername(wrappedFunc) {
let username = localStorage.getItem('username');
wrappedFunc(username);
}
不返回一个新的函数,外面怎么调用?
直白易懂
醍醐灌顶
666
不错不错
函数 wrapWithUsername (wrappedFunc) { let newFunc = () => { let username = localStorage.getItem('username'); WrappedFunc(用户名); }; 返回新函数; } 这样写不行吗? function wrapWithUsername(wrappedFunc) { let username = localStorage.getItem('username'); WrappedFunc(用户名); }
您仔细看,返回的是一个回调函数,类似函数柯里化
讲的太好了= =