React学习笔记(五) --- 组件生命周期和组件复用

一、组件生命周期

1、概念

​ 组件的生命周期是指组件从被创建到挂载到页面中,再到组件卸载的过程。在React中,只有类组件才有生命周期,函数组件是不存在生命周期的。组件生命周期大体分为三个阶段:创建阶段、更新阶段、卸载阶段。在组件生命周期的每一个阶段,React 都给我们提供了一些方法,让我们可以在对应阶段执行某些任务,这些方法就是生命周期的钩子函数。

在这里插入图片描述

2、创建阶段

​ 生命周期的创建阶段常用的钩子函数有三个,执行顺序为:constructor() --> render() --> componentDidMount

钩子函数触发时机作用
constructor()创建组件时,最先执行① 初始化state ② 为事件处理程序绑定this
render()每次组件渲染都会触发渲染页面UI(可以访问 props 和 state,但是不能在该函数中,调用setState() 方法)
componentDidMount()每次组件挂载(完成DOM渲染)后可以在此阶段发送网络请求和进行DOM操作

​ 之所以不能在render()方法内调用 setState() 方法,是因为 setState() 方法会改变组件的状态(数据),然后引起页面重新渲染,再次触发 render(),再次调用 setState() 方法…形成死循环。

3、更新阶段

​ 生命周期的更新阶段常用的钩子函数有两个,执行顺序为:render() --> componentDidUpdate() 。当组件调用 setState() / forceUpdate() / 接收新的props,这三种情况任意一种,就会触发组件的更新,重新渲染,进入更新阶段。

钩子函数触发时机作用
render()每次组件渲染都会触发渲染页面UI(可以访问 props 和 state,但是不能在该函数中,调用setState() 方法)
componentDidUpdate()组件更新(完成DOM渲染) 后可以在此阶段发送网络请求和进行DOM操作(如果调用setState() 必须放到一个if语句中)

​ 在 componentDidUpdate() 阶段调用 setState() 要加if条件判断也是为了避免死循环的出现。

4、卸载阶段

​ 生命周期的卸载阶段常用的钩子函数只有一个:componentWillUnmount():

钩子函数触发时机作用
componentWillUnmount()当组件卸载,从页面消失时触发执行清理工作(定时器、绑定的事件等)

二、组件复用

1、概念

​ 如果两个组件的主要部分的功能相似或相同,为了减少代码量,我们可以考虑组件复用,类似于函数封装,将相同的功能模块,封装到一个组件中去。在React中组件复用,复用的是组件的 state 和 操作state 的方法。

​ React 中实现组件复用的方式有两种:① render props 模式 ② 高阶组件(HOC) 。这两种方式并不是React提供的API,而是利用React的语法特点,利用技巧形成的固定写法。

2、render props 模式

​ 该模式的思路是将想要复用的state和操作state的方法封装到一个组件中,然后在使用组件时,给组件标签添加一个回调函数,该函数的参数就是组件内部向外传递的复用数据。在组件标签中,将该函数的返回值设置为要渲染的页面UI样式,然后在组件内部的render() 方法中,通调用该函数,获取想要渲染的页面UI样式。

具体步骤:

​ ① 创建组件,并在组件中写好要复用的状态逻辑代码(state 和 操作state 的方法)

​ ② 假设回调函数名为renderProps,将要复用的状态(state) 作为 props.renderProps() 方法的参数,暴露到组件外部。

​ ③ 在组件标签中,通过renderProps()方法的参数获取想要的数据,并通过返回值设置想要渲染的页面UI结构。

​ ④ 在组件中 把props.renderProps() 方法的返回值,作为要渲染的页面UI结构,放到 render() 方法的return中。

案例代码:
// 封装的复用组件 Mouse
export default class Mouse extends Component {
  // 想要复用的状态
  state = {
    x: 0,
    y: 0
  }
 // 在组件挂载时绑定事件
  componentDidMount() {
    window.addEventListener('mousemove', this.handleMouseMove)
  }
// 在组件卸载时解绑事件
  componentWillUnmount() {
    window.removeEventListener('mousemove', this.handleMouseMove)
  }
	// 状态操作程序
  handleMouseMove = e => {
    this.setState({
      x: e.clientX,
      y: e.clientY
    })
  }

  render() {
    const { x, y } = this.state

    return (
      <div>
        {this.props.renderProps({ x, y })}
      </div>
    )
  }
}

// 使用复用组件
import Mouse from './Mouse'

const Position = () => (
  <Mouse renderProps={ ({ x, y }) => (
      <p>
        鼠标当前位置:(x: {x}, y: {y})
      </p>
    ) }>
  </Mouse>
)

​ render props 模式除了可以通过属性回调函数的方式实现,还可以通过chidren 来实现,而且我更推荐这种实现方式:

// 封装的复用组件 Mouse
export default class Mouse extends Component {
	// 省略相同代码
  render() {
    const { x, y } = this.state

    return (
      <div>
        {/* 推荐使用 children */}
        {this.props.children({ x, y })}
      </div>
    )
  }
}

// 使用复用组件
import Mouse from './Mouse'

const Position = () => (
  <Mouse>
    {({ x, y }) => (
      <p>
        鼠标当前位置:(x: {x}, y: {y})
      </p>
    )}
  </Mouse>
)

​ 为了确保用户能够正确使用封装的复用组件,我们还可以给复用组件加上 props 校验:

// 用户必须传递children 并必须是函数
Mouse.protoTypes = {
  children: PropTypes.func.isRequired
}
3、高阶组件

​ 高阶组件(HOC,Higher-Order Component) 是实现组件复用的另一种方式,高阶组件本身就是一个函数,通过参数接收要包装的组件,返回增强后的组件。高阶组件内部会创建一个类组件,在这个类组件内提供要复用的状态和操作状态的方法,在render() 里面将传过来的参数组件,作为要渲染的页面UI结构。并通过prop将要复用的状态传递给参数组件。

具体步骤:

​ ① 创建一个高阶组件函数,一般约定名称以 with 开头,例如:withMouse() {}。

​ ② 指定函数的参数,参数表示要渲染的组件,该组件决定要渲染的页面UI结构,参数名称要首字母大写,因为后面要作为组件渲染。

​ ③ 在高阶组件函数内部创建一个类组件,提供要服用的状态逻辑代码,并return 这个类组件。

​ ④ 在上一步创建的类组件的render()中,渲染参数组件,并将状态state通过prop传递进参数组件内。

​ ⑤ 调用该高阶组件,传入要增强的组件,通过返回值获得增强后的组件,并将其渲染到页面中。

案例代码:
// 创建高阶组件
function withMouse(WrappedComponent) {
  // 该组件提供复用的状态逻辑
  class Mouse extends React.Component {
    // 鼠标状态
    state = {
      x: 0,
      y: 0
    }
	// 操作状态的方法
    handleMouseMove = e => {
      this.setState({
        x: e.clientX,
        y: e.clientY
      })
    }
    // 绑定事件 控制鼠标状态的逻辑 
    componentDidMount() {
      window.addEventListener('mousemove', this.handleMouseMove)
    }
	// 在组件卸载时解绑事件
    componentWillUnmount() {
      window.removeEventListener('mousemove', this.handleMouseMove)
    }
	// 渲染参数组件  并将状态传入组件内
    render() {
      return <WrappedComponent {...this.state} />
    }
  }
  // 返回创建的类组件
  return Mouse
}

// 要增强的组件
const Position = props => (
  <p>
    鼠标当前位置:(x: {props.x}, y: {props.y})
  </p>
)


// 获取增强后的组件:
const MousePosition = withMouse(Position)


class App extends React.Component {
  render() {
    return (
      <div>
        <h1>高阶组件</h1>
        {/* 渲染增强后的组件 */}
        <MousePosition />
      </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('root'))
设置displayName:

​ 如果用同一个高阶组件处理多个组件,会出现这多个组件在React的调试工具中的名字(displayName)是一样的,都是高阶组件中创建的那个类组件名字的情况。这是因为在默认情况下,React会使用这个类组件的名称作为 displayName 。为了方便我们调试和区分,我们可以在高阶组件中,通过参数组件来设置这个displayName:

// 在高阶组件内部
// 调用方法设置displayName
Mouse.displayName = `WithMouse${getDisplayName(WrappedComponent)}`
// 高阶组件外面 定义好处理方法
function getDisplayName(WrappedComponent) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}
传递 props:

​ 在使用高阶组件增强后的组件时,通过prop向组件内部传入一个参数,但我们却无法在要增强的组件中访问到这个参数,因为在高阶组件中只向下传递了本身的状态,并没有向下传递props中的参数。所以我们需要在高阶组件中将props也向下传递:

// 修改上面案例中高阶组件中类组件的render()   
render() {
      return <WrappedComponent {...this.state} {...this.props} />
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力的小朱同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值