劫持props
假设我们有一个原组件,它接收一个 name prop,并显示一个问候语:
// 原组件
function Greeting(props) {
return <h1> Hello, {props.name}! </h1>;
}
我们可以定义一个高阶组件,它可以通过 props 传递一个 color prop 给原组件,让原组件的文字颜色变化。同时,它也可以修改原组件的 name prop,让它变成大写。这样,我们就劫持了原组件的 props。
// 高阶组件
function withColor(WrappedComponent) {
return function (props) {
// 劫持 props
const newProps = { ...props, name: props.name.toUpperCase() };
// 劫持 render
const style = { color: props.color };
return (
<div style={style}>
<WrappedComponent {...newProps} />
</div>
);
};
}
然后,我们可以使用高阶组件来创建一个新的组件,它接收一个 color prop,并传递给原组件:
// 劫持后的组件
const ColoredGreeting = withColor(Greeting);
ReactDOM.render(
<ColoredGreeting name="Alice" color="red" />,
document.getElementById("root")
);
这样,我们就得到了一个显示红色文字,并且名字是大写的问候语组件。
劫持state
假设我们有一个原组件,它接收一个 count prop,并显示一个计数器:
// 原组件
function Counter(props) {
return (
<div>
<h1> Count: {props.count} </h1>
<button onClick={props.increment}> + </button>
<button onClick={props.decrement}> - </button>
</div>
);
}
我们可以定义一个高阶组件,它可以管理原组件的 count 状态,并提供 increment 和 decrement 方法给原组件,让原组件可以更新状态。这样,我们就劫持了原组件的 state。
// 高阶组件
function withState(WrappedComponent) {
return class extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
this.increment = this.increment.bind(this);
this.decrement = this.decrement.bind(this);
}
increment() {
this.setState((state) => ({ count: state.count + 1 }));
}
decrement() {
this.setState((state) => ({ count: state.count - 1 }));
}
render() {
// 劫持 state
const newProps = {
...this.props,
count: this.state.count,
increment: this.increment,
decrement: this.decrement,
};
return <WrappedComponent {...newProps} />;
}
};
}
然后,我们可以使用高阶组件来创建一个新的组件,它不需要接收任何 prop,因为它的状态已经由高阶组件管理了:
// 劫持后的组件
const StatefulCounter = withState(Counter);
ReactDOM.render(<StatefulCounter />, document.getElementById("root"));
这样,我们就得到了一个可以自增和自减的计数器组件。
劫持render
假设我们有一个原组件,它接收一个 title prop,并显示一个标题:
// 原组件
function Title(props) {
return <h1> {props.title} </h1>;
}
我们可以定义一个高阶组件,它可以在原组件的外面添加一个边框,修改原组件的样式,或者根据条件决定是否渲染原组件。这样,我们就劫持了原组件的 render。
// 高阶组件
function withBorder(WrappedComponent) {
return function (props) {
// 劫持 render
const style = { border: "1px solid black", padding: "10px" };
return (
<div style={style}>
<WrappedComponent {...props} />
</div>
);
};
}
function withStyle(WrappedComponent) {
return function (props) {
// 劫持 render
const style = { color: "red", fontSize: "24px" };
return <WrappedComponent {...props} style={style} />;
};
}
function withCondition(WrappedComponent) {
return function (props) {
// 劫持 render
if (props.title.length > 10) {
return <WrappedComponent {...props} />;
} else {
return null;
}
};
}
然后,我们可以使用高阶组件来创建一个新的组件,它会根据不同的高阶组件有不同的渲染效果:
// 劫持后的组件
const BorderedTitle = withBorder(Title);
const StyledTitle = withStyle(Title);
const ConditionalTitle = withCondition(Title);
ReactDOM.render(
<div>
<BorderedTitle title="Hello, world!" />
<StyledTitle title="Hello, world!" />
<ConditionalTitle title="Hello, world!" />
<ConditionalTitle title="Hello, everyone!" />
</div>,
document.getElementById("root")
);
这样,我们就得到了一个有边框的标题,一个红色字体的标题,和一个只在标题长度大于10时才显示的标题。
劫持生命周期
假设我们有一个原组件,它接收一个 data prop,并显示一个列表:
// 原组件
function List(props) {
return (
<div>
<ul>
{props.data.map((item, index) => {
return <li key={index}>{item}</li>;
})}
</ul>
</div>
);
}
我们可以定义一个高阶组件,它可以在原组件的 componentDidMount 和 componentWillUnmount 方法中执行一些操作,比如发送请求,设置定时器,添加事件监听等。这样,我们就劫持了原组件的生命周期。
// 高阶组件
function withLifecycle(WrappedComponent) {
return class extends React.Component {
constructor(props) {
super(props);
this.state = { data: [] };
this.fetchData = this.fetchData.bind(this);
this.handleResize = this.handleResize.bind(this);
}
componentDidMount() {
// 劫持生命周期
this.fetchData();
this.timer = setInterval(this.fetchData, 5000);
window.addEventListener("resize", this.handleResize);
}
componentWillUnmount() {
// 劫持生命周期
clearInterval(this.timer);
window.removeEventListener("resize", this.handleResize);
}
fetchData() {
fetch("/api/data")
.then((data) => {
this.setState({ data: data });
})
.catch((error) => {
console.error(error);
});
}
handleResize() {
console.log("Window resized");
}
render() {
return <WrappedComponent {...this.props} data={this.state.data} />;
}
};
}
然后,我们可以使用高阶组件来创建一个新的组件,它不需要接收任何 prop,因为它的数据和行为已经由高阶组件管理了:
// 劫持后的组件
const LifecycleList = withLifecycle(List);
ReactDOM.render(<LifecycleList />, document.getElementById("root"));
这样,我们就得到了一个可以自动更新数据,并且响应窗口大小变化的列表组件。