有时候,某些组建的各种功能及其处理逻辑几乎完全相同,只是显示的界面不一样,建议下面的方式选择其一来解决重复代码的问题(横切关注点)
实现这两个组建,我们通常的写法是:
// MoveBox
class MoveBox extends PureComponent {
state = {
x: 0,
y: 0
};
myRef = React.createRef();
handleMove = e => {
// e不是真实的dom对象,是经过包装的,不存在offsetX 或 offsetY
// 但是其存在 pageX (距离页面 pageY) or clientX(距离视口 clientY)
const { clientX, clientY } = e;
const { left, top } = this.myRef.current.getBoundingClientRect();
console.log(clientX - left);
this.setState({
x: clientX - left,
y: clientY - top
});
};
render() {
return (
<div className="moveBox" onMouseMove={this.handleMove} ref={this.myRef}>
<div
className="moveDiv"
style={{
left: `${this.state.x}px`,
top: `${this.state.y}px`
}}
></div>
</div>
);
}
}
// MoveMessage
class MoveMessage extends PureComponent {
state = {
x: 0,
y: 0
};
myRef = React.createRef();
handleMove = e => {
// e不是真实的dom对象,是经过包装的,不存在offsetX 或 offsetY
// 但是其存在 pageX (距离页面 pageY) or clientX(距离视口 clientY)
const { clientX, clientY } = e;
const { left, top } = this.myRef.current.getBoundingClientRect();
this.setState({
x: clientX - left,
y: clientY - top
});
};
render() {
return (
<div className="moveBox" onMouseMove={this.handleMove} ref={this.myRef}>
<p>
x:{this.state.x},y:{this.state.y}
</p>
</div>
);
}
}
// MoveContainer
class MoveContainer extends PureComponent {
render() {
return (
<div>
<MoveBox />
<MoveMessage />
</div>
);
}
}
会发现存在很多重复的代码
1.模仿context.Consumer
1.某个组件,需要某个属性
2.该属性是一个函数,函数的返回值用于渲染
3.函数的参数会传递为需要的数据
4.注意纯组件的属性(尽量避免每次传递的render props的地址不一致)
// MoveContainer
class MoveContainer extends PureComponent {
moveBox = value => (
<div
className="moveDiv"
style={{
left: `${value.x}px`,
top: `${value.y}px`
}}
></div>
);
moveMessage = value => (
<p>
x:{value.x},y:{value.y}
</p>
);
render() {
return (
<div>
<MoveIndex>{this.moveBox}</MoveIndex>
<MoveIndex>{this.moveMessage}</MoveIndex>
</div>
);
}
}
// MoveIndex 将MoveBox 和MoveMessage 合成一个组件,渲染内容用函数替代
class MoveIndex extends PureComponent {
state = {
x: 0,
y: 0
};
myRef = React.createRef();
handleMove = e => {
// e不是真实的dom对象,是经过包装的,不存在offsetX 或 offsetY
// 但是其存在 pageX (距离页面 pageY) or clientX(距离视口 clientY)
const { clientX, clientY } = e;
const { left, top } = this.myRef.current.getBoundingClientRect();
this.setState({
x: clientX - left,
y: clientY - top
});
};
render() {
return (
<div className="moveBox" onMouseMove={this.handleMove} ref={this.myRef}>
{this.props.children(this.state)}
</div>
);
}
}
2.render Props
1.某个组件,需要某个属性
2.该属性是一个函数,函数的返回值用于渲染
3.函数的参数会传递为需要的数据
4.注意纯组件的属性(尽量避免每次传递的render props的地址不一致)
5.通常该属性的名字加做render
写法与1相似,
// MoveContainer
class MoveContainer extends PureComponent {
moveBox = value => (
<div
className="moveDiv"
style={{
left: `${value.x}px`,
top: `${value.y}px`
}}
></div>
);
moveMessage = value => (
<p>
x:{value.x},y:{value.y}
</p>
);
render() {
return (
<div>
{/* 这个传入组件函数使用render这个属性 */}
<MoveIndex render={this.moveBox} />
<MoveIndex render={this.moveMessage} />
</div>
);
}
}
// MoveIndex
class MoveIndex extends PureComponent {
state = {
x: 0,
y: 0
};
myRef = React.createRef();
handleMove = e => {
// e不是真实的dom对象,是经过包装的,不存在offsetX 或 offsetY
// 但是其存在 pageX (距离页面 pageY) or clientX(距离视口 clientY)
const { clientX, clientY } = e;
const { left, top } = this.myRef.current.getBoundingClientRect();
this.setState({
x: clientX - left,
y: clientY - top
});
};
render() {
return (
<div className="moveBox" onMouseMove={this.handleMove} ref={this.myRef}>
{/* 组件内部 使用this.props.render接收 */}
{this.props.render(this.state)}
</div>
);
}
}
3.HOC
// MoveHoc
function MoveHoc(Comp) {
return class MoveIndex extends PureComponent {
state = {
x: 0,
y: 0
};
myRef = React.createRef();
handleMove = e => {
// e不是真实的dom对象,是经过包装的,不存在offsetX 或 offsetY
// 但是其存在 pageX (距离页面 pageY) or clientX(距离视口 clientY)
const { clientX, clientY } = e;
const { left, top } = this.myRef.current.getBoundingClientRect();
this.setState({
x: clientX - left,
y: clientY - top
});
};
render() {
return (
<div className="moveBox" onMouseMove={this.handleMove} ref={this.myRef}>
<Comp {...this.state} />
</div>
);
}
};
}
// MoveContainer
function MoveBox(props) {
return (
<div
className="moveDiv"
style={{
left: `${props.x}px`,
top: `${props.y}px`
}}
></div>
);
}
function MoveMessage(props) {
return (
<p>
x:{props.x},y:{props.y}
</p>
);
}
const HocMoveBox = MoveHoc(MoveBox);
const HocMoveMessage = MoveHoc(MoveMessage);
export default class MoveContainer extends PureComponent {
render() {
return (
<div>
<HocMoveBox />
<HocMoveMessage />
</div>
);
}
}