React 中的组件复用

2022-04-06T15:38:11.369Z阅读279
6

通过render props实现

  • 将要复用的state和操作state的方法封装到一个组件中
  • 在使用组件时,添加一个值为函数的prop,通常把这个prop命名为render,在组件内部调用这个函数。传进来的函数负责渲染UI
  • 在组件内部调用方法的时候,把状态当成参数进行传递

    Image 组件


import React, { Component } from 'react'

interface DemoProps {
  x: number
  y: number
}

export default class Demo extends Component<DemoProps> {
  render() {
    return (
      <img
        src={img}
        width="400px"
        style={{ position: 'absolute', left: this.props.x, top: this.props.y }}
        alt=""
      />
    )
  }
}

Position 组件


import React, { Component } from 'react'

interface PositionProps {
  x: number
  y: number
}

export default class Position extends Component<PositionProps> {
  render() {
    return (
      <>
        <h3>当前鼠标的位置</h3>
        <div>
          ({this.props.x},{this.props.y})
        </div>
      </>
    )
  }
}

Render Props


// 用于提供鼠标位置的逻辑,不负责渲染组件

import React, { Component, ReactPropTypes } from 'react'

interface MouseProps {
  render: (state: { x: number; y: number }) => React.ReactNode
}

export default class Mouse extends Component<MouseProps> {
  state = {
    x: 0,
    y: 0
  }
  move = (e: MouseEvent) => {
    this.setState({
      x: e.pageX,
      y: e.pageY
    })
  }

  componentDidMount() {
    document.addEventListener('mousemove', this.move)
  }
  componentWillUnmount() {
    document.removeEventListener('mousemove', this.move)
  }
  render() {
    return this.props.render(this.state)
  }
}

使用


import { lazy, useState, Suspense } from 'react'
import './App.css'

const Demo = lazy(() => import('@/components/demo'))
const Position = lazy(() => import('@/components/position'))
const Mouse = lazy(() => import('@/components/mouse'))

function App() {
  const [count, setCount] = useState(0)

  return (
    <div className="App">
      <Suspense fallback={<div>loading...</div>}>
        <Mouse render={({ x, y }) => <Demo x={x} y={y} />} />
        <Mouse render={({ x, y }) => <Position x={x} y={y}></Position>} />
      </Suspense>
    </div>
  )
}

export default App

children代替render属性

// scroll.tsx
import React, { Component } from 'react'

interface ScrollProps {
  children: (state: { top: number; left: number }) => React.ReactNode
}

export default class Scroll extends Component<ScrollProps> {
  state = {
    top: 0,
    left: 0
  }

  scroll = () => {
    this.setState({
      top: window.scrollX,
      left: window.scrollY
    })
  }

  componentDidMount() {
    window.addEventListener('scroll', this.scroll)
  }
  componentWillMount() {
    window.removeEventListener('scroll', this.scroll)
  }

  render() {
    return this.props.children(this.state)
  }
}

// app.tsx

<Scroll>
  {({ top, left }) => (
    <div
      style={{
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center',
        height: '1000px'
      }}
    >
      <span>
        {top}-{left}
      </span>
    </div>
  )}
</Scroll>

HOC

  • 高阶组件(HOC,Higher-Order Component)是一个函数,接收要包装的组件,返回增强后的组件
  • 高阶组件的命名: withMouse withRouter withXXX
  • 高阶组件内部创建一个类组件,在这个类组件中提供复用的状态逻辑代码,通过prop将复用的状态传递给被包装组件

使用步骤

  • 创建一个函数,名称约定以 with 开头
  • 指定函数参数(作为要增强的组件) 传入的组件只能渲染基本的UI
  • 在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回
  • 在内部创建的组件的render中,需要渲染传入的基本组件,增强功能,通过props的方式给基本组件传值
  • 调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面中

问题

  • 可能会导致props丢失

withMouse

import React from 'react'
// 参数 接受一个普通组件

export default function withMouse(Base: any) {
  class Mouse extends React.Component {
    state = {
      x: 0,
      y: 0
    }
    move = (e: MouseEvent) => {
      this.setState({
        x: e.pageX,
        y: e.pageY
      })
    }

    componentDidMount() {
      document.addEventListener('mousemove', this.move)
    }
    componentWillUnmount() {
      document.removeEventListener('mousemove', this.move)
    }

    render() {
      return <Base {...this.state} />
    }
  }

  return Mouse
}

app.tsx

const Position = lazy(() => import('@/components/position'))
const PWithMouse = withMouse(Position)

function App() {
  const [count, setCount] = useState(0)

  return (
    <div className="App">
      <Suspense fallback={<div>loading...</div>}>

        <DemoWithMouse />
        <PWithMouse />
        
      </Suspense>
    </div>
  )
}

export default App

*邮箱为选填, 若输入邮箱,您留言的回复,将在第一时间以邮件的形式通知您。
总共0条留言
No Data