含义
高阶组件就是一个函数,且该函数接收一个组件作为参数,并返回一个新的组件。
定义就是这么说的,但是看完的肯定是似懂非懂。继续往下走
HOF (高阶函数)
在了解高阶组件的时候,我们先来会议一下高阶函数,HOF(Higher-Order Function),这个的定义分为广义和侠义:
- 广义: 函数要是传入的参数中或者返回的结果都包含 函数 的函数;
- 侠义: 函数的传入参数和返回结果都要包含函数的函数
具体的应用: 函数的柯里化,回调,防抖,节流等
举个例子:
下面有两个函数,一个是看电视,另一个是看电影。但是都要获取用户名。
function watchMove(){ // 相同逻辑... const name = localStorage.getItem('name'); console.log(name + '看电影了'); } function watchTV(){ // 相同逻辑... const name = localStorage.getItem('name'); console.log(name + '看电视'); } watchMove(); watchTV()
上面我们大概看一下是不是有重复的代码,重复获取用户名。我们怎么样能够少写这个代码呢?
解决方法
function watchMove(name:string){ console.log(name + '看电影了'); } function watchTV(name:string){ console.log(name + '看电视'); } /** * 行为的高阶函数 * @param fn */ function action(fn:(name:string)=> void){ return function(){ const getName = localStorage.getItem('name') || '测试'; // 调用函数 fn(getName); } } action(watchMove)(); action(watchTV)();
结果
我们都知道,react 中的组件可以是类组件和函数组件,本质上都函数组件,只是类组件比函数组件多了点生命周期。
HOC(Higher-Order Components)
使用react类组件来改写上面的代码
import React, { Component } from 'react' /** * 状态接口 */ interface IState { name: string } /** * 看电视组件 */ class WatchTV extends Component<{}, IState>{ state = { name: '' } componentDidMount() { let name = localStorage.getItem('name') || '测试'; this.setState(pre => ({ name: name })) } render(){ return ( <div> {this.state.name} 看电视了 </div> ) } } /** * 看电影组件 */ class WatchMovie extends Component<{}, IState>{ state = { name: '' } componentDidMount() { let name = localStorage.getItem('name') || '测试'; this.setState(pre => ({ name: name })) } render(){ return ( <div> {this.state.name} 看电影了 </div> ) } }
效果
改造相同的部分
import React, { Component, ComponentType } from 'react' /** * 状态接口 */ interface IState { name: string } /** * 接口约定 */ type IProp = { name: string } /** * hoc * @param Comp ComponentType<IProp> 使用这个进行类型检查 */ const Action = (Comp: ComponentType<IProp>) => { return class Action extends Component<{}, IState>{ state = { name: '' } componentDidMount() { let name = localStorage.getItem('name') || '测试'; this.setState(pre => ({ name: name })) } render() { return <Comp name={this.state.name} /> } } } /** * 看电视组件 */ class WatchTV extends Component<IProp>{ render() { return ( <div> {this.props.name} 看电视了 </div> ) } } /** * 看电影组件 */ class WatchMovie extends Component<IProp>{ render() { return ( <div> {this.props.name} 看电影了 </div> ) } } const AWatchTv = Action(WatchTV); const AWatchMovie = Action(WatchMovie);
结果
注意:
1.不要在render中使用高阶组件,会递归无限循环
2.不要在高阶组件内部更改传入的组件,不符合单一原则
3.在ts 中的高阶组件中,类型需要使用 ComponentType<IProp> 使用这个进行类型检查,这样才不会丢失类型检查
4.高阶组件属于 装饰者 模式,可以利用HOC实现横切关注点, AOP