• 高阶函数&&高阶组件(二)


    高阶组件总共分为两大类

    • 代理方式
      1. 操纵prop
      2. 访问ref(不推荐)
      3. 抽取状态
      4. 包装组件
    • 继承方式
      1. 操纵生命周期
      2. 操纵prop

    代理方式之 操纵prop

    删除prop
    import React from 'react'
    function HocRemoveProp(WrappedComponent) {
      return class WrappingComPonent extends React.Component {
        render() {
          const { user, ...otherProps } = this.props;
          return <WrappedComponent {...otherProps} />
        }
      }
    }
    export default HocRemoveProp;

    增加prop

    import React from 'react'
    
    const HocAddProp = (WrappedComponent,uid) =>
      class extends React.Component {
        render() {
          const newProps = {
            uid,
          };
          return <WrappedComponent {...this.props}  {...newProps}  />
        }
      }
    
    export default HocAddProp;

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

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

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

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

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

    import React, { Component } from 'React';
    
    @HocRemoveProp 
    class SampleComponent extends Component {
    render() {}
    }
    export default SampleComponent;

    代理方式之 抽取状态

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

    const HocContainer = (WrappedComponent) =>
      class extends React.Component {
        constructor(props) {
          super(props)
          this.state = {
            name: ''
          }
        }
        onNameChange = (event) => {
          this.setState({
            name: event.target.value
          })
        }
        render() {
          const newProps = {
            name: {
              value: this.state.name,
              onChange: this.onNameChange
            }
          }
          return <WrappedComponent {...this.props} {...newProps} />
        }
      }
    @HocContainer
    class SampleComponent extends React.Component {
      render() {
        return <input name="name" {...this.props.name}/>
      }
    }

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

    代理方式之 包装组件

    const HocStyleComponent = (WrappedComponent, style) =>
      class extends React.Component {
        render() {
          return (
            <div style={style}>
              <WrappedComponent {...this.props} {...newProps} />
            </div>
          )
        }
      }

    这样使用

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

    -代理方式的生命周期的过程类似于堆栈调用:
    didmount 一> HOC didmount 一>(HOCs didmount) 一>(HOCs will unmount) 一>HOC will unmount一>unmount

    说继承方式之前先看一个例子

    const MyContainer = (WrappedComponent) =>
      class extends WrappedComponent {
        render() {
          return super.render();
        }
      }

    这个例子很简单,相当于把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 {
        render() {
          if (this.props.time && this.state.success) {
            return super.render()
          }
          return <div>倒计时完成了...</div>
        }
      }

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

    import * as React from 'react';
    import HocComponent from './HocComponent'
    
    @HocComponent
    
    class DemoComponent extends React.Component {
      constructor(props) {
        super(props);
       this.state = {
        success: true,
       };
     }
      render() {
        return   <div>我是一个组件</div>
      }
    } 
    export default DemoComponent;

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

    最后页面的效果就是,当然他不是循环的。先展示”我是一个组件“,我设置了两秒,之后展示”倒计时完成“.

    由此可以看出高阶组件也可以控制state

    但是最好要限制这样做,可能会让WrappedComponent组件内部状态变得一团糟。建议可以通过重新命名state,以防止混淆。

    继承方式之 操纵prop

    const HOCPropsComponent = (WrappedComponent) =>
      class extends WrappedComponent {
        render() {
          const elementsTree = super.render();
          let newProps = {
            color: (elementsTree && elementsTree.type === 'div') ? '#fff' : '#ff5555'
          };
    
          const props = Object.assign({}, elementsTree.props, newProps)
          const newElementsTree = React.cloneElement(elementsTree, props, elementsTree.props.children)
          return newElementsTree
        }
      }

    这样就传入了新的props,。
    React.cloneElement( element, [props], [...children])
    参数:TYPE(ReactElement),[PROPS(object)],[CHILDREN(ReactElement)]
    克隆并返回一个新的 ReactElement ,新返回的元素会保留有旧元素的 props、ref、key,也会集成新的 props。

    还有一个方式,在传递props上有着强于高阶组件的优势不用关心命名,
    class addProps extends React.Component {
      render() {
        const newProps = 'uid'
        return this.props.children(newProps)
      }
    } 

    使用方式

    <addProps>
    {
       (argument) => <div>{argument}</div>
    }
    </addProps>

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

    showUid(argument) {
        return <div>{argument}</div>
    }
    彩蛋recompose库

    recompose是一个很流行的库,它提供了很多很有用的高阶组件(小工具),而且也可以优雅的组合它们。

    Step 1 扁平props.

    我们有这样一个组件

    const Profile = ({ user }) => ( 
    <div> 
        <div>Username: {user.username}</div>  
        <div>Age: {user.age}</div>
     </div>
     )

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

    const Profile = ({ username,age }) => ( 
    <div> 
        <div>Username: {username}</div>  
        <div>Age: {age}</div>
     </div>
     )

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

    const enhance = compose(
     flattenProp('user'),
     renameProp('username', 'name')
     )

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

     const EnhancedProfile = enhance(Profile)
    还可以将 compose 函数用 在我们自己的高阶组件上,甚至结合使用都可以:
    const enhance = compose( 
     flattenProp('user'), 
     renameProp('username', 'name'), 
     withInnerWidth 
    )
    Step 2 提取输入表单的State

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

    import { withStateHandlers, compose } from "recompose";
    
    const initialState = {
      email: { value: "" },
      password: { value: "" },
      confirmPassword: { value: "" }
    };
    
    const onChangeEmail = props => event => ({
      email: {
        value: event.target.value,
        isDirty: true
      }
    });
    
    const onChangePassword = props => event => ({
      password: {
        value: event.target.value,
        isDirty: true
      }
    });
    
    const onChangeConfirmPassword = props => event => ({
      confirmPassword: {
        value: event.target.value,
        isDirty: true
      }
    });
    
    const withTextFieldState = withStateHandlers(initialState, {
      onChangeEmail,
      onChangePassword,
      onChangeConfirmPassword
    });
    
    export default withTextFieldState;

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

  • 相关阅读:
    字符串基本操作
    条件、循环、函数定义 练习
    turtle库基础练习
    Python基础练习
    AutoLayout 教程
    Mac上最佳的SVN管理工具:Cornerstone
    图片上传 关于压缩的问题
    关于单元测试的问题
    获取ios设备的当前IP地址
    关于项目使用ARC的管理方式
  • 原文地址:https://www.cnblogs.com/tommymarc/p/12020012.html
Copyright © 2020-2023  润新知