在所有路线上反应加载屏幕? [英] React loading screen on all routes?

查看:49
本文介绍了在所有路线上反应加载屏幕?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我了解如何使用 statecomponentDidMount() 获得微调屏幕,但是我将如何在所有路线之间创建加载屏幕而不必把componentDidMount()写在每个组件里?

我的 app.js 文件

class App 扩展组件 {构造函数(道具){超级(道具);this.child = React.createRef();}onWaypoint = () =>{this.child.current.navChangeHandler();}使成为() {让路线 = (<开关><路由精确路径="/projects" component={Projects}/><路由精确路径="/about" component={About}/><路由精确路径="/gallery" component={Gallery}/><路由精确路径="/" component={Home}/><精确重定向到="/"/></开关>)返回 (<浏览器路由器><div className="App" styleName="main-wrapper"><布局>{路线}<导航参考={this.child}/></布局>

</BrowserRouter>);}}

我希望能够在这些路线之间看到一个旋转器.

解决方案

您可以创建一个 HOC 子组件上的包装函数.然后,如果需要,您可以将 HOC 方法传递给孩子并让孩子调用该方法.

例如:

家长:

导出默认 WrappedComponent =>{类 Wrapper 扩展组件 {状态 = { isLoading: true };componentDidUpdate = prevProps =>{if (this.props.location !== prevProps.location) {this.setState({ isLoading: true });}};doneLoading = () =>this.setState({ isLoading: false })渲染 = () =>(<包裹组件doneLoading={this.doneLoading}isLoading={this.state.isLoading}{...this.props}/>);}返回 withRouter(Wrapper);};

Child(可以访问从 child 的 this.props 中传递下来的 HOC 方法/状态):

export default class Child extends PureComponent {componentDidMount = () =>{获取(someURL").then(response => response.json()).then(json => this.setState({ list: json }), () => this.props.doneLoading());};渲染 = () =>this.props.isLoading?<微调器/>: <DisplayList list={...this.state.list}/>}

缺点是每个子组件都需要导入 Spinner 并检查 isLoading 是否为 false.

限制

为了在大量嵌套的组件之间工作,您很可能希望/需要集成 Redux.此外,您需要让一个已安装的容器在每次路由更改时发送一个操作来更新/重置 isLoading Redux 状态.无论哪种方式,如果您想要 DRY,这都不是一个简单而优雅的解决方案.

<小时>

示例

工作示例(此示例仅通过超时设置/取消设置 HOC 状态):https://Codesandbox.io/s/2zo8lj44pr

Wrapper.js

import React, { Component, Fragment } from "react";从反应路由器"导入 { withRouter };从./Header"导入标题;从./Spinner"导入微调器;导出默认 WrappedComponent =>{类 Wrapper 扩展组件 {状态 = { 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);计时器 = () =>this.setState({ isLoading: false }, () => this.clearTimer());setTimer = () =>(this.timeout = setTimeout(this.timer, 3000));渲染 = () =>(<片段><标题/>{this.state.isLoading?<微调器/>: <WrappedComponent {...this.props}/></片段>);}返回 withRouter(Wrapper);};

index.js

从react"导入React;从react-dom"导入{渲染};从react-router-dom"导入 { BrowserRouter, Switch, Route };从../components/Home"导入Home;从../components/Schedule"导入计划;从./components/Wrapper"导入包装器;使成为(<浏览器路由器><开关><路由精确路径="/" component={Wrapper(Home)}/><Route path="/schedule" component={Wrapper(Schedule)}/></开关></BrowserRouter>,document.getElementById("root"));

I understand how I can get a spinner screen with state and componentDidMount() but how would I go about creating a loading screen in between on all routes without having to write componentDidMount() in every component?

My app.js file

class App extends Component {

  constructor(props) {
    super(props);
    this.child = React.createRef();
  }

  onWaypoint = () => {
    this.child.current.navChangeHandler();
  }

  render() {

    let routes = (
      <Switch>    
        <Route exact path="/projects" component={Projects} />
        <Route exact path="/about" component={About} />
        <Route exact path="/gallery" component={Gallery} />
        <Route exact path="/" component={Home} />
        <Redirect exact to="/" />
      </Switch>
    )

    return (
      <BrowserRouter>
        <div className="App" styleName="main-wrapper">
          <Layout>
              {routes}
              <Navigation ref={this.child} />
          </Layout>
        </div>
      </BrowserRouter>

    );
  }
}

I want to be able to see a spinner in between these routes.

解决方案

You can create a HOC wrapper function over a child component. Then, if desired, you can pass a HOC method to the child and have the child invoke the method.

For example:

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);
};

Child (has access to passed down HOC method/state from within child's 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} />
}

The downside is that every child component needs to import Spinner and check that isLoading is false.

Limitations

In order for this to work among heavily nested components, you'll most likely want/need to integrate Redux. In addition, you'll need to have a mounted container dispatch an action to update/reset the isLoading Redux state upon every route change. Either way, it's not going to be a simple and elegant solution if you want to be DRY.


Example

Working example (this example just has the HOC state being set/unset by a timeout): 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")
);

这篇关于在所有路线上反应加载屏幕?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆