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

一、组件生命周期

1、概念

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

React学习笔记(五) --- 组件生命周期和组件复用_第1张图片

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 (
      
{this.props.renderProps({ x, y })}
) } } // 使用复用组件 import Mouse from './Mouse' const Position = () => ( (

鼠标当前位置:(x: {x}, y: {y})

) }>
)

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

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

    return (
      
{/* 推荐使用 children */} {this.props.children({ x, y })}
) } } // 使用复用组件 import Mouse from './Mouse' const Position = () => ( {({ x, y }) => (

鼠标当前位置:(x: {x}, y: {y})

)}
)

​ 为了确保用户能够正确使用封装的复用组件,我们还可以给复用组件加上 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 
    }
  }
  // 返回创建的类组件
  return Mouse
}

// 要增强的组件
const Position = props => (
  

鼠标当前位置:(x: {props.x}, y: {props.y})

) // 获取增强后的组件: const MousePosition = withMouse(Position) class App extends React.Component { render() { return (

高阶组件

{/* 渲染增强后的组件 */}
) } } ReactDOM.render(, 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 
    }

你可能感兴趣的:(React,react.js,javascript,前端)