什么是高阶函数?
高阶函数的维基百科定义:至少满足以下条件之一:
- 接受一个或多个函数作为输入;
- 输出一个函数;
JavaScript中比较常见的filter、map、reduce都是高阶函数;
什么是高阶组件呢?
1. 高阶组件的英文是 Higher-Order Components,简称为 HOC; 2. 官方的定义:高阶组件是参数为组件,返回值为新组件的函数; 3. 组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件; 4.高阶组件并不是React API的一部分,它是基于React的 组合特性而形成的设计模式; 5. 高阶组件在一些React第三方库中非常常见: 比如redux中的connect; 比如react-router中的withRouter;
基本使用如下:
应用场景
场景一: props增强
- 不修改原组件的情况下,添加新的props:
import React, { PureComponent } from 'react';
// 定义一个高阶组件
function enhanceRegionProps(WrappedComponent) {
return (props) => {
// 所有组件公共的region
return <WrappedComponent {...props} region="中国" />;
};
}
class Home extends PureComponent {
render() {
return (
<h2>
Home:
{`昵称: ${this.props.nickname} 等级: ${this.props.level} 区域: ${this.props.region}`}
</h2>
);
}
}
class About extends PureComponent {
render() {
return (
<h2>
About:
<ul>
<li>昵称:{this.props.nickname}</li>
<li>等级:{this.props.level}</li>
<li>区域:{this.props.region}</li>
</ul>
</h2>
);
}
}
const EnhanceHome = enhanceRegionProps(Home);
const EnhanceAbout = enhanceRegionProps(About);
class App extends PureComponent {
render() {
return (
<div>
App
<EnhanceHome nickname="tsw" level={90} />
<EnhanceAbout nickname="wh" level={99} />
</div>
);
}
}
export default App;
- 利用高阶组件来共享context:
import React, { PureComponent, createContext } from 'react';
// 3. 定义一个高阶组件,接收context共享的数据
function withUser(WrappedComponent) {
return (props) => {
return (
<UserContext.Consumer>
{(user) => {
// 4. 传递到每个组件中:
return <WrappedComponent {...props} {...user} />;
}}
</UserContext.Consumer>
);
};
}
// 1. 创建Context
const UserContext = createContext({
nickname: '默认',
level: -1,
区域: '中国',
});
class Home extends PureComponent {
render() {
return (
<h2>
Home:{' '}
{`昵称: ${this.props.nickname} 等级: ${this.props.level} 区域: ${this.props.region}`}
</h2>
);
}
}
class Detail extends PureComponent {
render() {
return (
<ul>
<li>{this.props.nickname}</li>
<li>{this.props.level}</li>
<li>{this.props.region}</li>
</ul>
);
}
}
// 5. 高阶组件使用:
const UserHome = withUser(Home);
const UserDetail = withUser(Detail);
class App extends PureComponent {
render() {
return (
<div>
App
{/* 2.context 共享数据 */}
<UserContext.Provider
value={{ nickname: 'why', level: 90, region: '中国' }}
>
<UserHome />
<UserDetail />
</UserContext.Provider>
</div>
);
}
}
export default App;
场景二: 渲染判断鉴权
在开发中,我们可能遇到这样的场景:
某些页面是必须用户登录成功才能进行进入;
如果用户没有登录成功,那么直接跳转到登录页面;
// 高阶组件:
function withAuth(WrappedComponent) {
const NewCpn = props => {
const {isLogin} = props;
if (isLogin) { // 已登录- 展示正常页面组件:
return <WrappedComponent {...props}/>
} else { // 未登录-展示登录页面组件:
return <LoginPage/>
}
}
// 重新给组件命名:
NewCpn.displayName = "AuthCpn"
return NewCpn;
}
场景三: 生命周期劫持
开发中有时会遇到,多个组件页面需要记录渲染完成需要的时间;就可以通过高阶组件来抽离公共的功能; Vue中可以通过Mixin实现;
function withRenderTime(WrappedComponent) {
return class extends PureComponent {
// 即将渲染获取一个时间 beginTime
UNSAFE_componentWillMount() {
this.beginTime = Date.now();
}
// 渲染完成再获取一个时间 endTime
componentDidMount() {
this.endTime = Date.now();
const interval = this.endTime - this.beginTime;
console.log(`${WrappedComponent.name}渲染时间: ${interval}`)
}
render() {
return <WrappedComponent {...this.props}/>
}
}
}
高阶函数的意义
可以发现利用高阶组件可以针对某些React代码进行更加优雅的处理。
其实早期的React有提供组件之间的一种复用方式是mixin,目前已经不再建议使用
- Mixin 可能会相互依赖,相互耦合,不利于代码维护;
- 不同的Mixin中的方法可能会相互冲突;
- Mixin非常多时,组件是可以感知到的,甚至还要为其做相关处理,这样会给代码造成滚雪球式的复杂性;
- 当然,HOC也有自己的一些缺陷:
4.1 HOC需要在原组件上进行包裹或者嵌套,如果大量使用HOC,将会产生非常多的嵌套,这让调试变得非常困难;
4.2 HOC可以劫持props,在不遵守约定的情况下也可能造成冲突;
Hooks的出现,是开创性的,它解决了很多React之前的存在的问题!敬请期待Hooks~