首页 > 解决方案 > 从 NextJS 中的 _app 访问 React 上下文 API

问题描述

我需要访问_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?我想不出任何其他方式可以全局访问加载状态以有条件地加载特定组件。

标签: javascriptreactjsnext.js

解决方案


我不清楚为什么您需要上下文提供者成为_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));

推荐阅读