从NextJS中的_app访问React上下文API

时间:2020-10-02 22:19:17

标签: javascript reactjs next.js

我需要访问_app.js文件中的上下文API,以便设置由路由器事件触发的全局状态。这样做的原因是设置加载状态,整个应用程序中的各个组件都可以访问该加载状态。问题是_app.js文件是 提供的上下文,所以我没有上下文的上下文。

context.js

import React, { createContext, useState } from "react";

export const Context = createContext();

const ContextProvider = (props) => {
    const [isLoading, setIsLoading] = useState(false);

    return (
        <Context.Provider
            value={{
                isLoading,
                setIsLoading,
            }}
        >
            {props.children}
        </Context.Provider>
    );
};

export default ContextProvider;

_app.js

import React, { useContext } from "react";
import App from "next/app";
import Head from "next/head";
import Aux from "../hoc/Aux";
import ContextProvider, { Context } from "../context/context";
import { withRouter } from "next/router";

class MyApp extends App {
    static contextType = Context;

    componentDidMount() {
        this.props.router.events.on("routeChangeStart", () => {
            this.context.isLoading(true);
        });
        this.props.router.events.on("routeChangeComplete", () => {
            this.context.isLoading(false);
        });
        this.props.router.events.on("routeChangeError", () => {
            this.context.isLoading(false);
        });
    }

    render() {
        const { Component, pageProps } = this.props;

        return (
            <Aux>
                <Head>
                    <title>My App</title>
                </Head>
                <ContextProvider>
                    <Component {...pageProps} />
                </ContextProvider>
            </Aux>
        );
    }
}

export default withRouter(MyApp);

由于_app.js未包装在上下文提供程序中,因此这显然不起作用。我曾尝试将路由器事件侦听器进一步移到组件树的下面,但是那样一来,我就无法从主页到动态创建的页面获取加载状态。

是否有任何变通办法可以让我使用_app.js中的上下文?我想不出任何其他方式来全局访问加载状态以有条件地加载特定组件。

2 个答案:

答案 0 :(得分:0)

您可以使用nprogress显示加载指示器。例如:

import NProgress from "nprogress";
import Router from "next/router";

Router.onRouteChangeStart = () => NProgress.start();
Router.onRouteChangeComplete = () => NProgress.done();
Router.onRouteChangeError = () => NProgress.done();

Source

答案 1 :(得分:0)

我尚不清楚为什么您需要上下文提供者成为_app.js的父级(或完全独立的组件)。以下工作行不通?

class MyApp extends App {
  state = {
    isLoading: false,
  };

  componentDidMount() {
    this.props.router.events.on("routeChangeStart", () => {
      this.setIsLoading(true);
    });

    this.props.router.events.on("routeChangeComplete", () => {
      this.setIsLoading(false);
    });

    this.props.router.events.on("routeChangeError", () => {
      this.setIsLoading(false);
    });
  }

  render() {
    const { Component, pageProps } = this.props;

    return (
      <Aux>
        <Head>
          <title>My App</title>
        </Head>
        <Context.Provider
          value={{
            isLoading: this.state.isLoading,
            setIsLoading: this.setIsLoading,
          }}>
          <Component {...pageProps} />
        </Context.Provider>
      </Aux>
    );
  }

  setIsLoading = (isLoading) => {
    this.setState({ isLoading });
  }
}

export default withRouter(MyApp);

或者(如果我真的对您的用例不了解),可以创建一个HoC:

function withContext(Component) {
  return (props) => (
    <ContextProvider>
      <Component {...props} />
    </ContextProvider>
  );
}

class MyApp extends App {
  ...
}

export default withContext(withRouter(MyApp));