小能豆

How can I communicate with 2 reactjs components - using Jotai

javascript

I have 2 reactjs files:

  • Reports.js (used to request report and display the result)
  • AuthContext.js (has a socket connection to maintain communication with the backend server)

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;

UPDATE tried using jotai 2.0.0 but somehow no crash, but it didnt seem to go to handleReportloadingChange() function

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

  // ...
};

阅读 90

收藏
2023-11-30

共1个答案

小能豆

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.

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:

  1. Create a context in a separate file, let’s call it 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);
};
  1. Wrap your Reports component with the LoadingProvider in your main file (e.g., App.js):
import React from 'react';
import Reports from './Reports';
import { LoadingProvider } from './LoadingContext';

function App() {
  return (
    <LoadingProvider>
      <Reports />
    </LoadingProvider>
  );
}

export default App;
  1. Use the useLoading hook in both Reports.js and AuthContext.js:

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.

2023-11-30