高阶组件

亦凉 2023-06-17 11:53 115阅读 0赞

什么是高阶组件?

  • 高阶部位是一种用于复用组件逻辑的高级技术,它并不是React API的一部分。而是从React演化而来的一种模式。具体地说,高阶组件就是一个接受一个组件并返回另外一个新组件的函数。

这是官方文档说的,我并没有解劝,因为后面的解释会造成误解,但简单讲高阶组件(函数)就好比一个加工厂,同样的,屏幕、cpu、扬声器、键盘按键、外壳、电池,小米手机工厂组装完就是小米手机,魅族手机组装完就是魅族手机,基本材料都是相同的,不同工厂(高阶组件)有不同的实现及产出,当然这个工厂(高阶组件)也可能是针对某个基本材料的处理。总之产出的结果拥有了输入组件不具备的功能,输入的组件可以是一个组件的实例,也可以是一个组件类,还可以是一个无状态组件的函数。
解决什么问题了呢?
随着项目越来越复杂,开发过程中,多个组件需要某个功能,而且这个功能和页面并没有关系,所有也不能简单的抽取成一个新的组件,但是如果让同样的逻辑在各个组件里各自实现,无疑会导致重复的代码。比如页面有三种弹框一个有title,一个没有,一个又有右上角关闭按钮,除此之外别无它样,你总不能整好几个弹窗组件把,这里除了title,关闭按钮其他的就可以做为上面说的基本材料。
高阶组件总共分为两大类

  • 代理方式
    a. 操纵prop
    b. 访问ref(不推荐)
    c. 抽取状态
    d. 包装组件
  • 继承方法
    a. 操纵生命周期
    b. 操纵prop

一:代理方式之 操纵prop
删除prop

  1. import React from 'react'
  2. function HocRemoveProp(WrappedComponent) {
  3. return class WrappingComPonent extends React.Component {
  4. render() {
  5. const { user, ...otherProps } = this.props;
  6. return <WrappedComponent { ...otherProps} />
  7. }
  8. }
  9. }
  10. export default HocRemoveProp;`在这里插入代码片`

增加prop
接下来我把简化了写法,把匿名函数去掉,同时换成箭头函数

  1. import React from 'react'
  2. const HocAddProp = (WrappedComponentuid) =>
  3. class extends React.Component {
  4. render() {
  5. const newProps = {
  6. uid,
  7. };
  8. return <WrappedComponent { ...this.props} { ...newProps} />
  9. }
  10. }
  11. export default HocAddProp;

上面HocRemoveProp高阶组件中,所做的事情和输入组件WrappedComponent功能一样,只是忽略看名为user的prop。也就是说,如果WrappedComponent能处理名为user的prop,这个高阶组件返回的组件则完全无视这个prop;

  1. const { user, ...otherProps } = this.props;

这是一个利用es6语法技巧,经过上面的语句,otherProps里面就有this.props中所有的字段除了user。
假如我们现在不希望某个组件接受user的prop,那么我们就不要直接使用这个组件,而是把这个组件作为参数传递给HocRemoveProp,然后我们把这个函数的返回结果当作组件来使用两个高阶组件的使用方法:

  1. const newComponent = HocRemoveProp(SampleComponent);
  2. const newComponent = HocAddProp(SampleComponent,'1111111');

也可以利用decorator语法糖这样使用

  1. import React, { Component } from 'React';
  2. @HocRemoveProp
  3. class SampleComponent extends Component {
  4. render() { }
  5. }
  6. export default SampleComponent;

二:代理方式之 抽取状态
将所有的状态的管理交给外面的容器组件,这个模式就是 抽取状态
外面的容器就是这个高阶组件

  1. const HocContainer = (WrappedComponent) = >{
  2. class extends React.component {
  3. constructor(props){
  4. super(props)
  5. this.state = {
  6. name :""
  7. }
  8. }
  9. }
  10. onNameChange = (event) => {
  11. this.setState({
  12. name: event.target.value
  13. })
  14. }
  15. render(){
  16. const newProps={
  17. name:{
  18. value:this.state.name,
  19. onChange : this.onNameChange
  20. }
  21. }
  22. return <WrappedComponent { ...this.props} { ...newProps} />
  23. }
  24. }
  25. @HocContainer
  26. class SampleComponent extends React.Component {
  27. render() {
  28. return <input name="name" { ...this.props.name}/>
  29. }
  30. }

这样当我们在使用这个已经被包裹的input组件(SampleComponent)时候它的值就被放在了HocContainer高阶组件中,当很多这样的input组件都用这个HocContainer高阶组件时,那么它们的值都将保存在这个HocContainer高阶组件中
三:代理方式之 包装组件

  1. const HocStyleComponent = (WrappedComponent, style) =>
  2. class extends React.Component {
  3. render() {
  4. return (
  5. <div style={ style}>
  6. <WrappedComponent { ...this.props} { ...newProps} />
  7. </div>
  8. )
  9. }
  10. }

这样使用

  1. import HocStyleComponent from './HocStyleComponent';
  2. const colorSytle ={ color:'#ff5555'}
  3. const newComponent = HocStyleComponent(SampleComponent, colorSytle);

-代理方式的生命周期的过程类似于堆栈调用:
didmount 一> HOC didmount 一>(HOCs didmount) 一>(HOCs will unmount) 一>HOC will unmount一>unmount
在说继承方式之前先看一个例子

  1. const MyContainer = (WrappedComponent) =>
  2. class extends WrappedComponent {
  3. render() {
  4. return super.render();
  5. }
  6. }

这个例子很简单,相当于把WrappedComponent组件的render方法,通过super.render()方法吐到了MyContainer 中,可以顺序调用。

  • 继承方式的生命周期的过程类似于队列调用:
    didmount 一> HOC didmount 一>(HOCs didmount) 一>will unmount一>HOC will unmount一> (HOCs will unmount)
  • 代理方式下WrappedComponent会经历一个完整的生命周期,产生的新组件和参数组件是两个不同的组件,一次渲染,两个组件都会经历各自的生命周期,
  • 在继承方式下,产生的新组件和参数组件合二为一,super.render只是生命周期中的函数,变成一个生命周期。
    来看下面的例子你就会明白了。
    继承方式之 操纵生命周期(渲染劫持)
    首先创建一个高阶,在创建一个使用高阶组件的组件,也就是是输入组件,最后我在改变这个输入组件props

    import * as React from ‘react’;

    const HocComponent = (WrappedComponent) =>
    class MyContainer extends WrappedComponent {

    1. render() {
    2. if (this.props.time && this.state.success) {
    3. return super.render()
    4. }
    5. return <div>倒计时完成了...</div>
    6. }

    }

这个高阶组件会直接读取输入组件中的props,state,然后控制了输入组件的render展示
只有在props.time和state.success同时为真的时候才会展示

  1. import * as React from 'react';
  2. import HocComponent from './HocComponent'
  3. @HocComponent
  4. class DemoComponent extends React.Component {
  5. constructor(props) {
  6. super(props);
  7. this.state = {
  8. success: true,
  9. };
  10. }
  11. render() {
  12. return <div>我是一个组件</div>
  13. }
  14. }
  15. export default DemoComponent;

然后调用,递减time数值直到变为0

最后页面的效果就是,当然他不是循环的。先展示”我是一个组件“,我设置了两秒,之后展示”倒计时完成“.
但是最好要限制这样做,可能会让WrappedComponent组件内部状态变得一团糟。建议可以通过重新命名state,以防止混淆。
继承方式之 操纵prop

  1. const HOCPropsComponent = (WrappedComponent) =>
  2. class extends WrappedComponent {
  3. render() {
  4. const elementsTree = super.render();
  5. let newProps = {
  6. color: (elementsTree && elementsTree.type === 'div') ? '#fff' : '#ff5555'
  7. };
  8. const props = Object.assign({ }, elementsTree.props, newProps)
  9. const newElementsTree = React.cloneElement(elementsTree, props, elementsTree.props.children)
  10. return newElementsTree
  11. }
  12. }

这样就传入了新的props,。
React.cloneElement( element, [props], […children])
参数:TYPE(ReactElement),[PROPS(object)],[CHILDREN(ReactElement)]
克隆并返回一个新的 ReactElement ,新返回的元素会保留有旧元素的 props、ref、key,也会集成新的 props。
还有一个方式,在传递props上有着强于高阶组件的优势不用关心命名,

  1. class addProps extends React.Component {
  2. render() {
  3. const newProps = 'uid'
  4. return this.props.children(newProps)
  5. }
  6. }

使用方式

  1. <addProps>
  2. {
  3. (argument) => <div>{ argument}</div>
  4. }
  5. </addProps>

感觉很方便,但是每次渲染都会重新定义一个新的函数,如果不想的话就不要定义匿名函数,

  1. showUid(argument) {
  2. return <div>{ argument}</div>
  3. }

彩蛋recompose库
recompose是一个很流行的库,它提供了很多很有用的高阶组件(小工具),而且也可以优雅的组合它们。
Step 1 扁平props.
我们有这样一个组件

  1. const Profile = ({ user }) => (
  2. <div>
  3. <div>Username: { user.username}</div>
  4. <div>Age: { user.age}</div>
  5. </div>
  6. )

如果想要改变组件接口来接收单个 prop 而不是整个用户对象,可以用 recompose 提供的高 阶组件 flattenProp 来实现。

  1. const Profile = ({ usernameage }) => (
  2. <div>
  3. <div>Username: { username}</div>
  4. <div>Age: { age}</div>
  5. </div>
  6. )

const ProfileWithFlattenUser = flattenProp(‘user’)(Profile);
现在我们希望同时使用多个高阶组件:一个用于扁平化处理用户 prop,另一个用于重命名用 户对象的单个 prop,不过串联使用函数的做法似乎不太好。 此时 recompose 库提供的 compose 函数就派上用场了。

  1. const enhance = compose(
  2. flattenProp('user'),
  3. renameProp('username', 'name')
  4. )

然后按照以下方式将它应用于原有组件:

  1. const EnhancedProfile = enhance(Profile)

还可以将 compose 函数用 在我们自己的高阶组件上,甚至结合使用都可以:

  1. const enhance = compose(
  2. flattenProp('user'),
  3. renameProp('username', 'name'),
  4. withInnerWidth
  5. )

Step 2 提取输入表单的State
我们将从Recompose库中使用withStateHandlers高阶组件。 它将允许我们将组件状态与组件本身隔离开来。 我们将使用它为电子邮件,密码和确认密码字段添加表单状态,以及上述字段的事件处理程序。

  1. import { withStateHandlers, compose } from "recompose";
  2. const initialState = {
  3. email: { value: "" },
  4. password: { value: "" },
  5. confirmPassword: { value: "" }
  6. };
  7. const onChangeEmail = props => event => ({
  8. email: {
  9. value: event.target.value,
  10. isDirty: true
  11. }
  12. });
  13. const onChangePassword = props => event => ({
  14. password: {
  15. value: event.target.value,
  16. isDirty: true
  17. }
  18. });
  19. const onChangeConfirmPassword = props => event => ({
  20. confirmPassword: {
  21. value: event.target.value,
  22. isDirty: true
  23. }
  24. });
  25. const withTextFieldState = withStateHandlers(initialState, {
  26. onChangeEmail,
  27. onChangePassword,
  28. onChangeConfirmPassword
  29. });
  30. export default withTextFieldState;

withStateHandlers它接受初始状态和包含状态处理程序的对象。调用时,每个状态处理程序将返回新的状态。

发表评论

表情:
评论列表 (有 0 条评论,115人围观)

还没有评论,来说两句吧...

相关阅读

    相关 React-组件

    高阶组件是 React 中复用组件逻辑的一种技巧,高阶组件是一个函数,接收需要包装的组件,返回值为增强后的组件。 实现思路: 高阶组件内部创建一个类组件,在这个类组件中去提

    相关 组件

    什么是高阶组件? 高阶部位是一种用于复用组件逻辑的高级技术,它并不是React API的一部分。而是从React演化而来的一种模式。具体地说,高阶组件就是一个接受一个组

    相关 React组件

    高阶组件(HOC)是React里的高级技术为了应对重用组件的逻辑,HOC本质上不是React API的一部分。他是从React的组合性质中显露出来的模式 具体来说,一个高阶

    相关 React进组件

    一.介绍 1.说明: 高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。我的理解是定义一个A组件(函数),把这个A组件传入给B组件,B组件把A组件

    相关 react 组件

    高阶组件必须是一个函数,且必须返回一个组件,参数也必须接收一个组件 作用:能对传入的组件进行一些限定操作,可以使用生命周期等 ![在这里插入图片描述