Reputation: 5150
I was trying to implement a global loading screen when fetching data from an API, I read this answer and tried to implemented something similar.
loading.provider.js
import { createContext, useContext, useState } from "react";
const LoadingContext = createContext({
loading: false,
setLoading: null
});
export function LoadingProvider({ children }) {
const [loading, setLoading] = useState(false);
const value = { loading, setLoading };
return (
<LoadingContext.Provider value={value}>{children}</LoadingContext.Provider>
);
};
export function useLoading() {
const context = useContext(LoadingContext);
if (!context) {
throw new Error('useLoading must be used within LoadingProvider');
}
return context;
};
app.js
The LoadingScreen has been added to this screen:
import Layout from '@/components/Layout/Layout';
import { LoadingProvider, useLoading } from '@/Providers/loading.provider';
import LoadingScreen from "./LoadingScreen";
export default function App({ Component, pageProps }) {
const { loading } = useLoading();
return (
<>
<CssBaseline />
<LoadingProvider>
<AppStateProvider>
{ loading && <LoadingScreen loading={true} bgColor='#fff' spinnerColor={'#00A1FF'} textColor='#676767'></LoadingScreen> }
<Layout>
<Component {...pageProps} />
</Layout>
</AppStateProvider>
</LoadingProvider>
</>
);
};
I have a Layout component which holds a SideMenu
, top AppBar
and the main
components:
Layout.js
export default function Layout({ children }) {
return (
<Fragment>
<Box sx={{ display: 'flex' }}>
<AppBar />
<SideMenu />
<Box component="main" sx={mainContentWrapper}>
<div className={styles.container}>
<main className={styles.main}>
{children}
</main>
</div>
</Box>
</Box>
</Fragment>
);
}
Now I was trying to show it from a page ([identifier].js):
import { useLoading } from "@/Providers/loading.provider";
export default function MainPage({ response }) {
const { loading, setLoading } = useLoading();
const btnClickedAll = async (data) => {
setLoading(true);
};
return (
<Fragment>
<Button sx={{ ml: 2 }} key={'btnAll'} onClick={btnClickedAll} variant="contained">All</Button>,
</Fragment>
);
}
loading
is being called after setting it with setLoading(true)
but the state is not changing inside app.js. Can someone help me and point me to the right direction?
Edited: Made some changes in code according to @DustInComp suggestion.
Upvotes: 1
Views: 2143
Reputation: 555
The reason is that your LoadingProvider is inside of your app.js, and only children inside off the LoadingProvider would have access to the loading state.
Option 1
What you could do is to move wrap the entire app.js in LoadingProvider in the parent, maybe index.js.
In index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store';
import './index.css';
import './bootstrap.min.css';
import App from './App';
ReactDOM.render(
<LoadingProvider>
<App />
</LoadingProvider>,
document.getElementById('root')
);
OR
Option 2
You could create a child component of everything inside of app.js.
In Child.js (Name it as you please)
const Child = ({ Component, pageProps }) => {
const { loading } = useLoading();
<AppStateProvider>
{ loading && <LoadingScreen loading={true} bgColor='#fff' spinnerColor={'#00A1FF'} textColor='#676767'></LoadingScreen> }
<Layout>
<Component {...pageProps} />
</Layout>
</AppStateProvider>
};
export default Child;
And in app.js
export default function App({ Component, pageProps }) {
return (
<>
<CssBaseline />
<LoadingProvider>
<Child Component={Component} pageProps={pageProps}/>
</LoadingProvider>
</>
);
};
OR
Option 3
You could create a loader component of the loader and import it into app.js
.
In Loader.js (Name it as you please)
const Loader = () => {
const { loading } = useLoading();
return loading && <LoadingScreen loading={true} bgColor='#fff' spinnerColor={'#00A1FF'} textColor='#676767'></LoadingScreen>;
};
export default Loader;
And in app.js
export default function App({ Component, pageProps }) {
return (
<>
<CssBaseline />
<LoadingProvider>
<AppStateProvider>
<Loader />
<Layout>
<Component {...pageProps} />
</Layout>
</AppStateProvider>
</LoadingProvider>
</>
);
};
Upvotes: 1