Sunday, May 19, 2024
 Popular · Latest · Hot · Upcoming
0
rated 0 times [  2] [ 2]  / answers: 1 / hits: 6611  / 4 Years ago, fri, october 2, 2020, 12:00:00

I need to access the context API in my _app.js file in order to set global state triggered by the router events. The reason for this is to set a loading state which can be accessed by individual components throughout the app. The problem is is the context is provided from the _app.js file, so I don't have the context's context as it were.


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

Clearly this wouldn't work since _app.js is not wrapped in the context provider. I've tried moving the router event listeners further down the component tree, but then I don't get the loading state from my home page to my dynamically created pages that way.


Is there any workaround that lets me consume context in _app.js? I can't think of any other way I can access loading state globally to conditionally load specific components.


More From » reactjs

 Answers
4

It's not clear to me why you need the context provider to be a parent of _app.js (or a separate component at all). Wouldn't the following work?


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

Alternatively (if there's something I'm really not understanding about your use case), you could create a HoC:


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

class MyApp extends App {
...
}

export default withContext(withRouter(MyApp));

[#2566] Monday, September 28, 2020, 4 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
harleyterryp

Total Points: 290
Total Questions: 92
Total Answers: 95

Location: Montenegro
Member since Sun, May 7, 2023
1 Year ago
;