您可以创建一个HOC https://reactjs.org/docs/higher-order-components.html子组件的包装函数。然后,如果需要,您可以将 HOC 方法传递给子级并让子级调用该方法。
例如:
Parent:
export default WrappedComponent => {
class Wrapper extends Component {
state = { isLoading: true };
componentDidUpdate = prevProps => {
if (this.props.location !== prevProps.location) {
this.setState({ isLoading: true });
}
};
doneLoading = () => this.setState({ isLoading: false })
render = () => (
<WrappedComponent
doneLoading={this.doneLoading}
isLoading={this.state.isLoading}
{...this.props}
/>
);
}
return withRouter(Wrapper);
};
子级(可以访问从子级内部传递下来的 HOC 方法/状态this.props
):
export default class Child extends PureComponent {
componentDidMount = () => {
fetch("someURL")
.then(response => response.json())
.then(json => this.setState({ list: json }), () => this.props.doneLoading());
};
render = () =>
this.props.isLoading
? <Spinner />
: <DisplayList list={...this.state.list} />
}
缺点是每个子组件都需要导入Spinner
并检查一下isLoading
是假的。
局限性
为了使其能够在严重嵌套的组件中工作,您很可能希望/需要集成 Redux。此外,您需要让已安装的容器分派一个操作来更新/重置isLoading
每次路由更改时的 Redux 状态。不管怎样,如果你想保持 DRY,这都不会是一个简单而优雅的解决方案。
Example
工作示例(此示例仅通过超时设置/取消设置 HOC 状态):https://codesandbox.io/s/2zo8lj44pr https://codesandbox.io/s/2zo8lj44pr
包装器.js
import React, { Component, Fragment } from "react";
import { withRouter } from "react-router";
import Header from "./Header";
import Spinner from "./Spinner";
export default WrappedComponent => {
class Wrapper extends Component {
state = { isLoading: true };
componentDidMount = () => this.setTimer();
componentDidUpdate = prevProps => {
if (this.props.location !== prevProps.location) {
this.clearTimer();
this.setState({ isLoading: true }, () => this.setTimer());
}
};
clearTimer = () => clearTimeout(this.timeout);
timer = () => this.setState({ isLoading: false }, () => this.clearTimer());
setTimer = () => (this.timeout = setTimeout(this.timer, 3000));
render = () => (
<Fragment>
<Header />
{this.state.isLoading
? <Spinner />
: <WrappedComponent {...this.props} />
</Fragment>
);
}
return withRouter(Wrapper);
};
index.js
import React from "react";
import { render } from "react-dom";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import Home from "../components/Home";
import Schedule from "../components/Schedule";
import Wrapper from "./components/Wrapper";
render(
<BrowserRouter>
<Switch>
<Route exact path="/" component={Wrapper(Home)} />
<Route path="/schedule" component={Wrapper(Schedule)} />
</Switch>
</BrowserRouter>,
document.getElementById("root")
);