简体   繁体   中英

How to rewrite async HOC component to the React Hooks?

I'm using a template for my React app, and I came across one component that constantly gives me the following warning:

Warning: componentWillMount has been renamed, and is not recommended for use. See https:... for details.

Now, I'm trying to rewrite this component to React Hooks, to avoid multiple warnings in the console.

This is how the component looks like:

import React, { Component } from 'react';
import Nprogress from 'nprogress';
import ReactPlaceholder from 'react-placeholder';
import 'nprogress/nprogress.css';
import 'react-placeholder/lib/reactPlaceholder.css';
import CircularProgress from '../components/CircularProgress/index';

export default function asyncComponent(importComponent) {
  class AsyncFunc extends Component {
    constructor(props) {
      super(props);
      this.state = {
        component: null,
      };
    }

    componentWillMount() {
      Nprogress.start();
    }

    componentWillUnmount() {
      this.mounted = false;
    }

    async componentDidMount() {
      this.mounted = true;
      const { default: Component } = await importComponent();
      Nprogress.done();
      if (this.mounted) {
        this.setState({
          component: <Component {...this.props} />,
        });
      }
    }

    render() {
      const Component = this.state.component
        || (
        <div
          className="loader-view"
          style={{ height: 'calc(100vh - 200px)' }}
        >
          <CircularProgress />
        </div>
        );
      return (
        <ReactPlaceholder type="text" rows={7} ready={Component !== null}>
          {Component}
        </ReactPlaceholder>
      );
    }
  }

  return AsyncFunc;
}

And here is the example of its usage:

import React from 'react';
import { Route, Switch, withRouter } from 'react-router-dom';
import asyncComponent from '../util/asyncComponent';

const Routes = ({ match }) => (
  <Switch>
    <Route
      path={`${match.url}/main`}
      component={asyncComponent(() => import('./routes/MainPage/index'))}
    />
    {/* <Route component={asyncComponent(() => import("app/routes/extraPages/routes/404"))}/> */}
  </Switch>
);


export default withRouter(Routes);

I know how to rewrite component lifecycle methods ( componentDidMount , componentWillUnmount should be rewritten to useEffect ), but I don't understand part with props - asyncComponent gets importComponent as a prop, but where are we getting props in AsyncFunc? And how this could be rewritten to the functional component?

As I understood, asyncComponent is a Higher Order Component that responds with an updated component.

Sorry for not providing a sandbox for this example.

I couldn't test this code but I think is a solution

import React, { useState, useEffect } from 'react';
import Nprogress from 'nprogress';
import ReactPlaceholder from 'react-placeholder';
import 'nprogress/nprogress.css';
import 'react-placeholder/lib/reactPlaceholder.css';
import CircularProgress from '../components/CircularProgress/index';

const asyncComponent = importComponent => {
  const [component, setComponent] = useState(null);

  Nprogress.start();

  useEffect(async () => {
    const { default: Component } = await importComponent();
    Nprogress.done();
    setComponent(<Component {...importComponent} />);
  }, []);

  return component ? (
    <ReactPlaceholder type="text" rows={7} ready>
      {component}
    </ReactPlaceholder>
  ) : (
    <div className="loader-view" style={{ height: 'calc(100vh - 200px)' }}>
      <CircularProgress />
    </div>
  );
};

export default asyncComponent;

I don't see the need to use the state mounted because you only use it in the dismount to setState component, but if 2 lines before you set mounted as true, it is not necessary to generate a re-render, you can go and setState component directly.

I hope this helps you.

According to reactjs.org, componentWillMount will not be supported in the future. https://reactjs.org/docs/react-component.html#unsafe_componentwillmount

There is no need to use componentWillMount.

Before discussing your question but where are we getting props in AsyncFunc?

start with an example

const A = props => {
  return <p>{JSON.stringify(props.location)}</p>;
};

function App() {
  return (
    <Switch>
      <Route exact path="/" component={A} />
      <Route exact path="/a" component={p => <A {...p} />} />
    </Switch>
  );
}
  • Route / and route /a both components are getting props but in different ways
  • on route / the props are automatically passed to the component

As you know HOC take a component and in response, it returns a component

so asyncComponent(() => import('./routes/MainPage/index') will return AsyncFunc

so we can simply say that

  <Route
      path={`${match.url}/main`}
      component={AsyncFunc}
  />

and that's all

component={AsyncFunc} is equal to component={(p) => <AsyncFunc {...p} />}

and that how pros are passing

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM