封装组件必然是为了逻辑复用,那么下面我们通过使用不同的封装方式去封装一个猫(HOC)、鼠(Render Props)、狗(Hook)跟着鼠标移动的逻辑组件,来看看组件封装的奥妙。
一:HOC(高阶组件)
高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。
高阶组件其实就是一个参数是组件,返回值是新组件的函数。
import React from "react";
// HOC withSubscription 函数 WrappedComponent 需要被封装的组件
const withSubscription = WrappedComponent => {
return class WithSubscription extends React.Component {
constructor(props) {
super(props)
this.state = {
x: 0,
y: 0,
}
}
// 获取位置信息
getSite = (e) => {
this.setState({
x: e.clientX,
y: e.clientY,
})
}
componentDidMount() {
document.addEventListener('mousemove', this.getSite)
}
componentWillUnmount() {
document.removeEventListener('mousemove', this.getSite)
}
render() {
return (
<WrappedComponent {...this.props} {...this.state} />
)
}
}
}
// 对 CatTracker 进行装饰, 使其获得获取位置信息的能力
@withSubscription
class CatTracker extends React.Component {
constructor(props) {
super(props)
}
render() {
const {x, y} = this.props
return <div style={{position: 'absolute', left: x, top: y }}>猫</div>
}
}
export default CatTracker
二:Render Props
Render Props是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术
import React from "react";
function Cat(Props) {
const { x, y } = Props.mouse;
return <div style={{ position: "fixed", left: x + 50, top: y + 50 }}>鼠</div>;
}
class Mouse extends React.Component {
constructor(props) {
super(props);
this.state = {
x: 0,
y: 0,
};
}
// 获取位置信息
getSite = (e) => {
this.setState({
x: e.clientX,
y: e.clientY,
});
};
componentDidMount() {
document.addEventListener("mousemove", this.getSite);
}
componentWillUnmount() {
document.removeEventListener("mousemove", this.getSite);
}
render() {
return (
<div>
{this.props.render(this.state)}
</div>
);
}
}
function MouseTracker() {
return (
<>
<h1>鼠标!</h1>
<Mouse render={(mouse) => <Cat mouse={mouse} />} />
</>
);
}
export default MouseTracker;
通过props传递组件,有点类似slot的思想
三:Hook
Hook作用于函数组件,且我们可以使用自定义Hook.
import { useState, useEffect } from "react";
const useDog = () => {
const [x, setX] = useState(0)
const [y, setY] = useState(0)
// 获取位置信息
const getSite = (e) => {
setX(e.clientX)
setY(e.clientY)
}
useEffect(() => {
document.addEventListener('mousemove', getSite)
return () => {
document.removeEventListener('mousemove', getSite)
}
})
return {
x,
y
}
}
const DogTracker = () => {
const site = useDog()
return (
<div style={{position: 'fixed', left: site.x + 100, top: site.y + 100}}>狗</div>
)
}
export default DogTracker
通过对同一逻辑进行不同方式的封装,Hook无疑看起来逻辑更清晰,复用更简单,而函数组件也更符合react的设计初衷。