Вы можете создать функцию-оболочку HOC над дочерним компонентом.Затем, если необходимо, вы можете передать метод 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 при каждом изменении маршрута.В любом случае, это не будет простым и элегантным решением, если вы хотите быть СУХИМЫМ.
Пример
Рабочий пример (в этом примере просто установлено / не установлено состояние HOC)по таймауту): https://codesandbox.io/s/2zo8lj44pr
Wrapper.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")
);