示例场景:获取鼠标当前的位置
一、HOC(高阶组件)
示例:
// MousePosition.js HOC封装函数
import React, { Component } from 'react';
export default WrappedComponent => {
return class extends Component {
constructor(props){
super(props)
this.state = {
positionX: 0,
positionY: 0
}
}
componentDidMount() {
document.addEventListener('mousemove', (e) => {
this.setState({
positionX: e.clientX,
positionY: e.clientY
});
})
}
render(){
return <WrappedComponent {...this.props} {...this.state} />;
}
}
}
// MousePoint.js 位置信息展示
import React, { Component } from 'react';
import mousePositionHoc from './hoc/MousePosition';
class MousePoint extends Component{
constructor(props){
super(props);
}
render(){
return(
<div>
<span>鼠标的横坐标{this.props.positionX}</span>
<span>鼠标的纵坐标{this.props.positionY}</span>
</div>
)
}
}
export default mousePositionHoc(MousePoint)二、Render Props
示例:
// MousePoint.js
import React, { Component } from 'react';
export default class MousePoint extends Component {
constructor(props) {
super(props);
this.state = {
positionX: 0,
positionY: 0
};
}
componentDidMount() {
document.addEventListener('mousemove', (e) => {
this.setState({
positionX: e.clientX,
positionY: e.clientY
});
})
}
render() {
return (
<div>{this.props.render(this.state)}</div>
);
}
}
// 父组件
import React, { Component } from 'react';
import MousePoint from './MousePoint';
export default class MouseTracker extends Component{
constructor(props){
super(props);
}
render(){
return(
<div>
<MousePoint
render={(state) => {
return(
<div>
<span>鼠标横坐标是{state.positionX}</span>
<span>鼠标纵坐标是{state.positionY}</span>
</div>
)
}}
/>
</div>
)
}
}三、children
示例:
import React, { Component } from 'react';
export default class ChildComponent extends Component{
constructor(props){
super(props);
}
render(){
return(
<div>{this.props.children}</div>
)
}
}
function App() {
return (
<div className="App">
<ChildComponent>
<p>Hello World</p>
</ChildComponent>
</div>
);
}四、Hook
示例:
// useMousePosition.js Hook
import React, { useState, useEffect } from 'react';
export default () => {
const [positionX, setPositionX] = useState(0);
const [positionY, setPositionY] = useState(0);
const getMousePosition = (e) => {
setPositionX(e.clientX);
setPositionY(e.clientY);
};
useEffect(() => {
document.addEventListener('mousemove', getMousePosition);
return () => {
document.removeEventListener('mousemove', getMousePosition);
};
});
return {
positionX: positionX,
positionY: positionY
}
}
// 使用
import React, { useState, useEffect } from 'react';
import useMousePosition from './useMousePosition';
export default () => {
const mousePosition = useMousePosition();
return(
<div>
<span>鼠标的横坐标{mousePosition.positionX}</span>
<span>鼠标的纵坐标{mousePosition.positionY}</span>
</div>
)五、注意事项
1、父组件不能使用props传输整个组件实例或refs给子组件,这样会破坏封装
示例:
class App extends React.Component {
constructor(props) {
super(props);
this.state = { number: 0 };
}
render() {
return (
<div className="app">
<span className="number">{this.state.number}</span>
<Controls parent={this} />
</div>
);
}
}
class Controls extends React.Component {
render() {
return (
<div className="controls">
<button onClick={() => this.updateNumber(+1)}>Increase</button>
<button onClick={() => this.updateNumber(-1)}>Decrease</button>
</div>
);
}
updateNumber(toAdd) {
this.props.parent.setState(prevState => ({
number: prevState.number + toAdd
}));
}
}示例优化:
class App extends Component {
constructor(props) {
super(props);
this.state = { number: 0 };
}
render() {
return (
<div className="app">
<span className="number">{this.state.number}</span>
<Controls
onIncrease={() => this.updateNumber(+1)}
onDecrease={() => this.updateNumber(-1)}
/>
</div>
);
}
updateNumber(toAdd) {
this.setState(prevState => ({
number: prevState.number + toAdd
}));
}
}
const Controls = ({ onIncrease, onDecrease }) => {
return (
<div className="controls">
<button onClick={onIncrease}>Increase</button>
<button onClick={onDecrease}>Decrease</button>
</div>
);
};2、容器组件负责状态的管理和数据的请求,UI组件负责页面的渲染
示例:
// 容器组件 负责状态的管理和数据的请求
import axios from 'axios';
class WeatherFetch extends Component {
constructor(props) {
super(props);
this.state = { temperature: 'N/A', windSpeed: 'N/A' };
}
render() {
const { temperature, windSpeed } = this.state;
return (
<WeatherInfo temperature={temperature} windSpeed={windSpeed} />
);
}
async componentDidMount() {
const response = await axios.get('http://weather.com/api');
const { current } = response.data;
this.setState({
temperature: current.temperature,
windSpeed: current.windSpeed
});
}
}
// UI组件 负责页面的渲染
function WeatherInfo({ temperature, windSpeed }) {
return (
<div className="weather">
<div>Temperature: {temperature}°C</div>
<div>Wind: {windSpeed} km/h</div>
</div>
);
}3、HOC偏好单一责任原则
HOC的一个常见用法是为封装的组件增加新属性或修改现有的属性值。这种技术称为属性代理。
示例:
function withNewFunctionality(WrappedComponent) {
return class NewFunctionality extends Component {
render() {
const newProp = 'Value';
const propsProxy = {
...this.props,
// 修改现有属性:
ownProp: this.props.ownProp + ' was modified',
// 增加新属性:
newProp
};
return <WrappedComponent {...propsProxy} />;
}
}
}
const MyNewComponent = withNewFunctionality(MyComponent);你还可以通过控制输入组件的渲染过程从而控制渲染结果。这种 HOC 技术被称为渲染劫持。
示例:
function withModifiedChildren(WrappedComponent) {
return class ModifiedChildren extends WrappedComponent {
render() {
const rootElement = super.render();
const newChildren = [
...rootElement.props.children,
// 插入一个元素
<div>New child</div>
];
return cloneElement(
rootElement,
rootElement.props,
newChildren
);
}
}
}
const MyNewComponent = withModifiedChildren(MyComponent);你还可以通过HOC的属性代理技术实现职责的分离。
示例:
// 展示表单字段和附加的事件处理程序由PersistentForm承担
class PersistentForm extends Component {
constructor(props) {
super(props);
this.state = { inputValue: props.initialValue };
this.handleChange = this.handleChange.bind(this);
this.handleClick = this.handleClick.bind(this);
}
render() {
const { inputValue } = this.state;
return (
<div className="persistent-form">
<input type="text" value={inputValue} onChange={this.handleChange} />
<button onClick={this.handleClick}>Save to storage</button>
</div>
);
}
handleChange(event) {
this.setState({
inputValue: event.target.value
});
}
handleClick() {
this.props.saveValue(this.state.inputValue);
}
}
// 查询和保存到本地存储的职责由 withPersistence()HOC承担
function withPersistence(storageKey, storage) {
return function (WrappedComponent) {
return class PersistentComponent extends Component {
constructor(props) {
super(props);
this.state = { initialValue: storage.getItem(storageKey) };
}
render() {
return (
<WrappedComponent
initialValue={this.state.initialValue}
saveValue={this.saveValue}
{...this.props}
/>
);
}
saveValue(value) {
storage.setItem(storageKey, value);
}
}
}
}
// 使用
const LocalStoragePersistentForm = withPersistence('key', localStorage)(PersistentForm);
const instance = <LocalStoragePersistentForm />;
// 你可以轻松地将存储类型更改为session storage
const SessionStoragePersistentForm = withPersistence('key', sessionStorage)(PersistentForm);
const instance = <SessionStoragePersistentForm />;