手摸👋,带你理解 React 高阶组件

572 阅读3分钟

首先说一下,不会这个也可以完成很多需求,但是拥有这个思维模式后,相信你写的代码就更加优雅,。高阶组件是学习 React 的一大难点,我开始也卡了很久,被绕晕,理解不了,但是写过几次后就觉得它的设计是如此精巧,让人叹服。

高阶组件的定义

不知道你用过高阶函数没,其实高阶组件是一个套路,就是把函数换成了组件。 翻译:高阶组件就是一个函数,它接收一个组件作为参数,并且返回一个新的组件。

我们定义了两个简单的组件来分别显示用户名和年龄,如下可以显示我们需要的

// 显示用户名的组件
const ShowName = () => (
    <div>{localStorage.getItem('username')}</div>
)

// 显示年龄的组件
const ShowAge = () => (
    <div>{localStorage.getItem('age')}</div>
)

但是现在产品说:这两个模块都要加入当前的时间信息,于是,代码就变成了这样。你看,下面的代码就有大量的冗余,我们当然可以提取显示时间的公共代码为一个组件,在父组件里直接引用子组件,这也是我在使用 React 经常使用的套路。

// username.js
class Username extends React.Component {
   state = { time: new Date() }
    componentDidMount() {
      this.timerID = setInterval(() => this.tick(), 1000)
    }

    componentWillUnmount() {
      clearInterval(this.timerID);
    }

    tick() {
      this.setState({
        time: new Date()
      });
    }
    
    render() {
      return (
        <div>
            <div>{localStorage.getItem('username')}</div>
            <h2>{this.state.time}</h2>
        </div>
      )
    }
}


// 显示年龄的组件 age.js
class Age extends React.Component {
   state = { time: new Date() }
    componentDidMount() {
      this.timerID = setInterval(() => this.tick(), 1000)
    }

    componentWillUnmount() {
      clearInterval(this.timerID);
    }

    tick() {
      this.setState({
        time: new Date()
      });
    }
    
    render() {
      return (
        <div>
            <div>{localStorage.getItem('age')}</div>
            <h2>{this.state.time}</h2>
        </div>
      )
    }
}

但是还有一种思维是极具有技巧性的,它同样可以达到组件复用甚至灵活处理不同的需求,它对我们的原组件不做任何的修改。首先,我们定义一个没有副作用的纯函数组件。于是乎,我们就可以在高阶组件封装的这一层展示我们想要的功能,如 username.js 展现时间和名字,age.js 展现时间和年龄,当然,想要什么都可以随意写,是不是感觉很爽,很舒服。

// withTimer.js   高阶组件
const WithTimer = (WrapComponent) => {
    return class extends React.Component {
       state = { time: new Date() }
        componentDidMount() {
          this.timerID = setInterval(() => this.tick(), 1000)
        }
    
        componentWillUnmount() {
          clearInterval(this.timerID);
        }
    
        tick() {
          this.setState({
            time: new Date()
          });
        }
        render() {
          return <WrapComponent time={this.state.time} {...this.props} />;
        }
    }
}

// username.js
import WithTimer from './withTimer'
class Username extends React.Component {
    render() {
        return (
            <div>
                <div>{localStorage.getItem('username')}</div>
                <h2>{this.props.time.toLocaleString() + 'username'}</h2>
            </div>
        )
    }
}
export default WithTimer(Username)

// age.js
import WithTimer from './withTimer'
class Age extends React.Component {
    render() {
        return (
            <div>
                <div>{localStorage.getItem('age')}</div>
                <h2>{this.props.time.toLocaleString() + 'age'}</h2>
            </div>
        )
    }
}
export dafault WithTimer(Age)

总结

相对于 porps 属性传递给子组件,高阶组件可以让我们开发时实现更复杂的应用,这里的 demo 相对简单,逻辑不复杂,很多复杂的时候可以让我们的代码更加灵活和具有扩展性。自己仔细品一下,高阶组件就是一个纯函数,让我们把横切面的功能抽出来。

在 react-redux,ant 的form 表单中,都使用到了。它可以让我们的原组件的 props 上具有其他的属性可以扩展。 好了,更多的用法,还要自己慢慢品。

// connect是一个返回函数的函数(就是个高阶函数)
const enhance = connect(mapStateToProps, mapDispatchToProps)
// 返回的函数就是一个高阶组件,该高阶组件返回一个与Redux store
// 关联起来的新组件
const ConnectedComment = enhance(Component)

const WrappedNormalLoginForm = Form.create()(NormalLoginForm)