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