react 高阶函数详解(附送彩蛋)

此文适合react新手入门,react大佬能够略过(毕竟之前都是写vue,React才写了一个多月, 掩面泪奔)html

主要是学习react中的一些经验总结,若是你以为对你有帮助,能够给个赞githubvue

react项目入门react

react版本:16.0.0 (由于工做中仍是15的版本)git

首先咱们先来讲说 有状态组件和无状态组件github

有状态组件 无状态组件

有状态组件:组件内部状态发生变化,须要state来保存变化。redux

无状态组件:组件内部状态是不变的,用不到state。建议写成函数组件后端

组件设计思路:经过定义少部分有状态组件管理整个应用的状态变化,而且将状态经过props传递给其他无状态组件。 设计模式

有状态组件主要关注处理状态变化的业务逻辑,无状态组件主要关注组件UI渲染工做。这样更有利于组件的复用,组件之间解耦更加完全数组

这样有时就会产生一个问题,若是给UI组件加不一样的逻辑怎么办?bash

2种比较好的方法, this.props.children 和 HOC , 具体实例。那么下面就来详细说说 HOC 高阶组件

知识前置:

装饰器设计模式:容许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是做为现有的类的一个包装。

这种模式建立了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

HOC 高阶组件 (封装并分离组件的通用逻辑。其实就是装饰器设计模式的应用)

  • 基本概念:

在JS中高阶函数能够接受一个函数做为参数,返回值做为也是函数的函数。相似的 高阶组件也能够接受一个组件为参数,返回一个被加工过的组件。

本质上高阶函数是一个函数,而不是组件。

  • 使用场景:
  1. 操纵props
  2. 经过ref访问组件的实例
  3. 组件状态提高
  4. 用其余元素包装组件

例子:

  1. 操纵props 这个用的比较多

在被包装的组件接收到props以前。高阶组件能够先拦截到props,对props执行增删改等操做,而后将修改过的props传给被包装组件。

import React, { Component } from 'react'

function withPersistentData (wrapedComponent) {
  return class extends Component {
    componentWillMount() {
      let data = localStore.getItem('data');
      this.setState({data})
    }

    render () {
      const { data } = this.state;
      // 经过{...this.props} 把传给当前组件的值继续传给被包装的组件
      return <wrapedComponent data={data} {...this.props} />
    }
  }
}

@withPersistentData
export default class myComponent extends Component {
  render() {
    return (
      <div>
        {this.props.data}
      </div>
    )
  }
}

复制代码
  1. 经过ref访问组件的实例。这个用法我是比较少用到。

高阶组件经过ref获取被包装的组件实例的引用,而后高阶组件就具有了直接操做被包装组件的属性和方法的能力。

function withRef (wrapedComponent) {
  return class extends Component {
    someMethod = () => {
      // this.wrapedComp        被包装组件实例 
      // someMethodWrapedComp   被包装组件的方法
      this.wrapedComp.someMethodWrapedComp()
    }

    render () {
      // 经过{...this.props} 把传给当前组件的值继续传给被包装的组件
      // 给被包装的组件添加ref属性,获取被包装组件实例并赋值给this.wrapedComp
      return <wrapedComponent ref={(comp) =>{this.wrapedComp = comp}} {...this.props} />
    }
  }
}
复制代码
  1. 组件状态提高

上面已经说过 无状态组件更容易被复用,咱们能够利用高阶组件将本来受控组件中须要本身维护的的状态统一提高到高阶组件中,受控组件无状态化。

import React, { Component } from 'react'

function withControlledComp(wrappedComp) {
  state = {
    value : null,
  }
  handleValueChange = (e) => {
    this,setState({value: e.target.value})
  }

  render () {
    const newProps ={
      controlledProps: {
        value: this.state.value,
        onChange: this.handleValueChange
      }
    }
    return <wrappedComp {...this.props} {...newProps} />
  }
}

@withControlledComp
class ControlledComp extends Component {
  render () {
    // 此时的受控组件为无状态组件,状态由高阶组件控制
    return <input {...this.props.controlledProps} />
  }
}
复制代码
  1. 用其余元素包装组件
function withRedColor (wrapedComponent) {
  return class extends Component {
    render () {
      return (<div style={color: 'red}><wrapedComponent {...this.props} /> </div>) } } } 复制代码



* 参数传递 高阶组件的参数除了接受组件,还能够接受其余参数。

在第一个操做props的例子里,若是要获取key值不肯定时,这个组件就不知足了。

咱们通常采用这种方式:HOC(...params)(wrappedComp)

function withPersistentData = (key) => (wrapedComponent) => {
  return class extends Component {
    componentWillMount() {
      let data = localStore.getItem(key);
      this.setState({data})
    }

    render () {
      const { data } = this.state;
      // 经过{...this.props} 把传给当前组件的值继续传给被包装的组件
      return <wrapedComponent data={data} {...this.props} />
    }
  }
}

class myComponent extends Component {
  render() {
    return (
      <div>
        {this.props.data}
      </div>
    )
  }
}

// 获取key=‘data’的数据
const myComponentWithData = withPersistentData('data')(myComponent)

// 获取key=‘name’的数据
const myComponentWithData = withPersistentData('name')(myComponent)
复制代码

实际上这种形式的高阶组件大量出如今第三方的库中,例如react-redux中的connect函数

connect(mapStateToProps, mapDispatchToProps)(wrappedComponent)
复制代码



* 注意事项
  1. 不要在render中使用高阶组件,也尽可能不要在其余的生命周期函数中使用高阶组件。 由于高阶组件每次返回的都是一个新组件,因而每次render,前一次建立的组件都会被卸载,本次建立的组件会被从新挂载。
  2. 若是须要使用被包装组件的静态方法,就必需要手动复制这些方法。由于高阶组件不包含被包装组件的静态方法。
  3. Refs不回被传递给被包装组件。
  4. 与父组件的区别。若是这部分逻辑与UI/DOM相关,那么这部门逻辑适合放在父组件中实现;若是逻辑与DOM不直接相关,那么这部分逻辑适合放在高阶组件的抽象中。例如数据校验请求发送等

彩蛋来了,彩蛋就是react生命周期的使用场景,哈哈哈哈哈,惊不惊喜,意不意外。

react组件 的生命周期 以及 使用场景

挂载阶段

1. constructor
2. componentWillMount
3. render
4. componentDidMount
复制代码

使用场景

1. coustructor  
  一般用于初始化组件的state以及绑定事件的处理方法(好比bind(this))等

2. componentWiillMound
  在组件被挂载到DOM前调用,且只会调用一次,
  实际项目中比较少用到,由于能够在该方法中的执行的均可以提到coustructor中
  在这个方法中this.setState不会引发从新渲染

3. render
  渲染方法。
  注意:render只是返回一个UI的描述,真正渲染出页面DOM的工做由react本身完成
  
4. componentDidMount
  在组件被挂载到DOM后调用,且只会调用一次,
  一般用于像后端请求数据
  在这个方法中this.setState会引发组件的从新渲染

复制代码

更新阶段

1. componentWillReceiveProps
2. shouldComponentUpdate
3. componentWillUpdate
4. render
5. componentDidUpdate
复制代码

使用场景

1. componentWillRceiveProps(nextProps)
  这个方法只在props引发组件更新时调用。
  通常会比较一下this.props和nextProps来决定是否执行props变化后的逻辑
  好比:根据新的props调用this.setState来触发组件的从新渲染

2. shouldComponentUpdate(nextProps,nextState)
  这个方法决定组件是否继续执行更新过程。 
  默认是true,继续更新;false阻止更新。
  通常是经过比较nextPops,nextState和当前组件的props,state来决定返回结果。 
  这个方法能够减小没必要要的渲染,优化组件性能。
  缘由:根据渲染流程,首先会判断shouldComponentUpdate是否须要更新。若是须要更新,调用render方法生成新的虚拟DOM与旧的虚拟DOM进行对比(render只是返回一个UI描述),若是对比不一致,则根据最小粒度改变去更新DOM。

3. componentWillUpdate
  render前调用,组件更新前执行某些逻辑的地方。
  通常不多用到。

4. render

5. componentDidUpdate(prevProps, prevState)
  组件更新后被调用,能够做为操做更新后DOM的地方。
  这个方法中的prevProps和prevState表明组件中更新前的props和state。

注意:在render前的生命周期中,componentWillReceiveProps,shouldComponentUpdate,componentWillUpdate中的this.state依然指向更新前的state。
复制代码

销毁阶段

componentWillUnmount
  组件卸载前被调用。
  清除定时器,清除componentDidMount中手动建立的DOM,取消请求等
复制代码

结束语

最后感谢能看到这里的朋友,由于水平有限,若是有错误敬请指正,十分感激。

参考:
react进阶之路

相关文章
相关标签/搜索