I have 2 reactjs files:
user first goes to the report page generated by Reports.js and then there is a call to the backend server which returns right away but it shows a loading spinner. When the report is completed, it will send the data to AuthContext.js. However, I have trouble from AuthContext.js to be able to request a call setReportLoading() in Reports.js for the purpose of stopping the loading spinner. can you please advise how to solve this ?
I tried the method below setReportLoading() but it has this error:
setReportLoading.js:115 Uncaught TypeError: setReportLoading is not a function
here is my code snippet
In file AuthContext.js import { setReportLoading } from 'Reports'; export const AuthProvider = ({ children }) => { socket.on('processmessage', (msg) => { if (msg.type == 'checking'){ setReportLoading(2); } }); } In file Reports.js const Reports = () => { const [loading, setLoading] = useState(1) const setReportLoading = (isLoading) => { setLoading(isLoading); }; const renderContent = () => { if (loading === 1) return ( <div className="d-flex justify-content-center align-items-center" style={{ minHeight: '200px' }}> <div className="spinner-border text-primary" role="status"> <span className="visually-hidden">{t('loc_react_loading')}</span> </div> </div> ) if (loading === 2) return ( <div className="mb-4 card border-0 shadow-sm" style={{'min-width': '900px'}}> {renderReportSelection()} {showReport && renderReport()} </div> ) } return ( <DashboardLayout> </DashboardLayout> ) } export default Reports;
in AuthContext.js import { atom, useAtom } from 'jotai'; export const reportloadingAtom = atom(1); export const AuthProvider = ({ children }) => { const [reportloading, setReportloading] = useAtom(reportloadingAtom); socket.on('processmessage', (msg) => { if (msg.type == 'checking') { setReportloading(2); } }); // ... }; in Reports.js import { useAtom } from 'jotai'; import { reportloadingAtom } from 'AuthContext' const Reports = () => { const [reportloading, setReportloading] = useAtom(reportloadingAtom); const [loading, setLoading] = useState(1); useEffect(() => { // This function will run every time the value of reportloadingAtom changes function handleReportloadingChange() { console.log("handleReportloadingChange") setLoading(reportloading); } }, [reportloading]); // ... };
It looks like you are trying to use a function (setReportLoading) from Reports.js in AuthContext.js. This is generally not a recommended approach, as it can lead to circular dependencies and issues related to the order of execution.
setReportLoading
Reports.js
AuthContext.js
Instead, you should consider using a state management solution like React Context or a state management library like Redux to manage the loading state across components.
Here is a simplified example using React Context:
LoadingContext.js
import React, { createContext, useContext, useState } from 'react'; const LoadingContext = createContext(); export const LoadingProvider = ({ children }) => { const [loading, setLoading] = useState(1); const setReportLoading = (isLoading) => { setLoading(isLoading); }; return ( <LoadingContext.Provider value={{ loading, setReportLoading }}> {children} </LoadingContext.Provider> ); }; export const useLoading = () => { return useContext(LoadingContext); };
Reports
LoadingProvider
App.js
import React from 'react'; import Reports from './Reports'; import { LoadingProvider } from './LoadingContext'; function App() { return ( <LoadingProvider> <Reports /> </LoadingProvider> ); } export default App;
useLoading
In Reports.js:
import React, { useEffect } from 'react'; import { useLoading } from './LoadingContext'; const Reports = () => { const { loading, setReportLoading } = useLoading(); useEffect(() => { // Your existing code to handle loading changes // ... // Example: simulate loading completion setTimeout(() => { setReportLoading(2); }, 2000); }, [setReportLoading]); // The rest of your Reports component // ... }; export default Reports;
In AuthContext.js:
import { useLoading } from './LoadingContext'; export const AuthProvider = ({ children }) => { const { setReportLoading } = useLoading(); // Your existing code // ... socket.on('processmessage', (msg) => { if (msg.type === 'checking') { setReportLoading(2); } }); // ... };
This way, you can manage the loading state in a centralized manner using the LoadingContext, and both Reports and AuthContext can interact with it without introducing circular dependencies.
LoadingContext
AuthContext