【问题标题】:React page infinitely refreshes the page every secondReact 页面每秒无限刷新页面
【发布时间】:2021-11-06 23:25:47
【问题描述】:

我已经使用 React 构建了一个发票仪表板系统。我有另一个使用 React 构建的应用程序版本,我遇到了同样的问题。

我已经注释掉了对我的 API 的所有调用,useEffect(),它仍然每秒重新加载页面。任何建议都会有所帮助。

发票仪表板

import React, { Fragment, useState, useEffect }  from 'react'
import { useAuth0 } from "@auth0/auth0-react";
import axios from 'axios';
import LogRocket from 'logrocket';
import 'react-notifications/lib/notifications.css';
import {NotificationContainer, NotificationManager} from 'react-notifications';
import { Menu, Transition } from '@headlessui/react'
import {
  BellIcon,
  MenuAlt2Icon
} from '@heroicons/react/outline'
import { SearchIcon } from '@heroicons/react/solid'

//Material UI
import { makeStyles } from "@material-ui/core/styles";
import Button from '@material-ui/core/Button';
import Tabs from '@material-ui/core/Tabs';
import Tab from '@material-ui/core/Tab';
import Typography from '@material-ui/core/Typography';
import Box from '@material-ui/core/Box';
import StorageIcon from '@material-ui/icons/Storage';
import MessageIcon from '@material-ui/icons/Message';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
import styles from "../css/jss/material-dashboard-react/views/dashboardStyle";

//components
import Card from '../Tailwind-Components/Card';
import Snackbar from '../Material-Components/Snackbar';
import Sidebar  from '../Tailwind-Components/Sidebar';

const userNavigation = [
    { name: 'Your Profile', href: '#' },
    { name: 'Settings', href: '#' },
    { name: 'Sign out', href: '#' },
  ]
  
  function classNames(...classes) {
    return classes.filter(Boolean).join(' ')
  }

export default function InvoiceDashboard() {
  
  axios.defaults.withCredentials = true;


  const [sidebarOpen, setSidebarOpen] = useState(false)

  // Abstracted Authentication variables
  const { user, isAuthenticated, isLoading }= useAuth0();
  
  // User Invoices 
  const [data, setData] = useState([{}]);
  // Users Paid Invoices
  const [paidData, setPaidData] = useState([{}]);
  // The currently logged in user to be sent to server 
  const [loggedInUser, setLoggedInUser] = useState({});

  // Positioning State for notification
  const [tc, setTC] = useState(true);
  const [bl, setBL] = useState(true);

  // Tabs state
  const [value, setValue] = useState(0);

  // Modal open state
  const [open, setOpen] = useState(true);


  // Styling
  const useStyles = makeStyles(styles);

  // Time Conversions
  const moment = require('moment'); 
  moment().format(); 


 


  // Calls the backend to capture the users open invoices
  async function fetchUserInvoices() {
    try {
       // Making a call to external api
       const url = `${process.env.GATSBY_FETCH_USER_INVOICES}`;
       const axiosConfig = {
         method: 'get',
         url,
       };
       const invoiceResponse = await axios(axiosConfig).catch((e) => { console.log(e) });
       setData(invoiceResponse?.data); 
      
    } catch (error) {
  
      console.log('[fetchUserInvoices] An Error has occured:', error);
    }
     
    return;
   }

  // Calls the backend to capture the users paid invoices
  async function fetchPaidInvoices() {
    
    try {
      
      const url = `${process.env.GATSBY_FETCH_PAID_INVOICES}`;
      const axiosConfig = {
        method: 'get',
        url,
      };
      const invoices = await  axios(axiosConfig).catch((e) => {console.log(e)});
      await setPaidData(invoices.data);
      console.log('[fetchPaidInvoices] Paid invoices for user fetched from server');
    } catch (error) {
      
      console.log('[fetchPaidInvoices]: An Error has occured', error)
    }
  }

 


 
  // calls the backend and sends the current user object
  async function sendLoggedInUser(user) {
    
    try {
    
      const url = `${process.env.GATSBY_SAVE_USER}`;
      const axiosConfig = {
        method: 'post',
        url,
        data: user
      };

    await  axios(axiosConfig).catch((e) => {console.log(e)});
    console.log('[sendLoggedInUser]: LoggedInUser sent to server');
    return;
    } catch (error) {
      
      console.log('[sendLoggedInUser]: An Error has Occured', error);
      return;
    }
  };

  // Works just as the function above only the paid invoices endpoint utilizes the information
  async function sendPaidInvoiceUser(current) {
    
    try {
    
      const url = `${process.env.GATSBY_SEND_PAID_INVOICE_USER}`;
      const axiosConfig = {
        method: 'post',
        url,
        data: current
      };

    await  axios(axiosConfig).catch((e) => {console.log(e)});
    console.log('[sendPaidInvoiceUser]: paid Invoice User sent to server');
    return;
    } catch (error) {
      
      console.log('[sendPaidInvoiceUser]: An Error has Occured', error);
      return;
    }
  };

  // Calls the backend endpoint that gets the invoices from stripe and the DB and activates the logic
  async function updateDB() {
    
    try {

      const url = `${process.env.GATSBY_STORE_INVOICES}`;
      const axiosConfig = {
        method: 'get',
        url,
      };
    await  axios(axiosConfig).catch((e) => {console.log(e)});
      
    } catch (error) {
      
      console.log('[updateDB]: An Error has occured', error)
    }
  };

    // Closes the Invoice reminders
    const handleClose =  () => {

      setOpen(false);
    };

    // saves the users preference for reminders to local Storage as a Boolean
   function userPreference(data){
    
     localStorage.setItem('notification', data);
     
    };

    // Retreives the users preference from localStorage and executes a conditional based on the Boolean
    function userPreferenceGet() {
      const preference = localStorage.getItem('notification');
       if(preference){
        

        console.log('[userPreferenceGet] Preference:', preference)
         return true;
        } else {

          console.log('[userPreferenceGet] Preference:', preference)
         return false;
       }
    };
    // Holds the UI of the alert that allows the User to choose their reminder notifcation preference
    function notifcationSettings() {
      return (
        <Dialog
        open={open}
        onClose={handleClose}
        aria-labelledby="alert-dialog-title"
        aria-describedby="alert-dialog-description"
      >
        <DialogTitle id="alert-dialog-title">{"Would you like Invoice reminders?"}</DialogTitle>
        <DialogContent>
          <DialogContentText id="alert-dialog-description">
            Let 02Pilot remind you of invoices that are due in 2 days! We promise we wont spam you!
          </DialogContentText>
        </DialogContent>
        <DialogActions>
          <Button 
          id="notificationYes"
          onClick={() => {
            handleClose()
            userPreference(true)
          }} 
          color="primary">
            Yes ????
          </Button>
          <Button 
          id="notificationNo"
          onClick={() => {
            handleClose()
            userPreference(false)
          }} 
          color="secondary">
            No ????
          </Button>
        </DialogActions>
      </Dialog>
      )
    }

  

 

  useEffect(() => {
    
    // App rerenders if the user is authenticated.
    if (isAuthenticated) {
     setLoggedInUser(user);
    }

  }, [ isAuthenticated])



  try {

    
    
    // sends call to backend to update database with any invoices that may be paid.
      updateDB();

      // executes POST call to backend with the logged in user.
       sendLoggedInUser(loggedInUser);
      sendPaidInvoiceUser(loggedInUser);
      
     

  } catch (error) {
    console.log(error);
  }
  

setTimeout(() => {

  // executes function that fetches invoices from DB after 100 milliseconds. Had to account for invoices to get fetched.
  fetchUserInvoices();

}, 100) 




   // Function that controls notifications.
   const showNotification = (place) => {
    switch (place) {
      case "bl":
        if (bl) {
          setTimeout(function() {
            setBL(false);
          }, 2000);
        }
        break;

      default:
        break;
    }
  };
  
   // Tab handle change function
   const handleChange = (event, newValue) => {
    setValue(newValue);
    if(newValue == 1) {

      //fetchPaidInvoices();
      return;
    } else {

      return;
    }
    
  };

  // Controls Tabs panel
  function TabPanel(props) {
    const { children, value, index, ...other } = props;
  
    return (
      <div
        role="tabpanel"
        hidden={value !== index}
        id={`simple-tabpanel-${index}`}
        aria-labelledby={`simple-tab-${index}`}
        {...other}
      >
        {value === index && (
          <Box p={3}>
            <Typography>{children}</Typography>
          </Box>
        )}
      </div>
    );
  }
  
  // Controls index of tabs
  function a11yProps(index) {
    return {
      id: `simple-tab-${index}`,
      'aria-controls': `simple-tabpanel-${index}`,
    };
  }

  // Function adds a comma and a decimal to numbers that need it.
  function decimalInsert(num) {
    const internationalNumberFormat = new Intl.NumberFormat('en-US')
    var num = (num/100).toFixed(2);
   return internationalNumberFormat.format(num)
  };

  // Check the size of an object, has the same functionality of Array.length()
  Object.size = function(obj) {
    var size = 0,
      key;
    for (key in obj) {
      if (obj.hasOwnProperty(key)) size++;
    }
    return size;
  };

  // The conditional checks the size of the object and if the Users information exists then it is sent to LogRocket  

  if(process.env.NODE_ENV === 'production'){

    if(Object.size(loggedInUser) > 0 ) {
      LogRocket.identify(loggedInUser.sub, {
        name: loggedInUser.name,
        email: loggedInUser.email
      });
    }
  }
  

  
 
  // Function that sorts the invoices from newest to oldest
  function biggestToSmallest(a, b) {
    return b.created - a.created;
  }

  // Shows the reminder for the invoices that are due in 2 days
  function reminderNotification(data) {
    
      // Converting epoch seconds to human readable date
      const invoiceDue = moment.unix(data.due_date);
      // Subtracting 2 days from the due date (for the reminder)
      const reminderDate = invoiceDue.subtract(2, 'days');
      // Change the format of the date
      const reminderConverted = reminderDate.format(' MMMM Do YYYY');
      // Get the current date
      const currentDate = moment();
      // Change the format of the current date
      const currentDateConverted = currentDate.format(' MMMM Do YYYY');
      
    
        if(reminderConverted === currentDateConverted ) {
          return (
            
           NotificationManager.warning(`${data.id} is due in 2 days: Please visit the Dashboard ????`, 'Invoices Due', 2000)
          )
         } else {
           console.log('[card] No invoices are due at this time');
         }
  return;    
  };

  // Orders invoices by newest to oldest
  if(data && paidData) {
    data.sort(biggestToSmallest);
    paidData.sort(biggestToSmallest);
  }
  

  function card(data) {
    
    // Converting epoch seconds to human readable date
    const invoiceCreated = moment.unix(data.created);
    const invoiceCreatedConverted = invoiceCreated.format(' MMMM Do YYYY');
    const invoiceDue = moment.unix(data.due_date);
    const invoiceDueConverted = invoiceDue.format(' MMMM Do YYYY');
    
    // Executing the function to convert amout due.
    const amountDue = decimalInsert(data.amount_due);
    
   
    // Shows a notification when data from database has been loaded
    return(
      
        <div> 
          { data ? showNotification("bl") : null}
        <Card
                    invoiceId={data.id}
                    invoiceCreated={invoiceCreatedConverted}
                    invoiceDue={invoiceDueConverted}
                    amountDue={amountDue}
                    invoiceUrl={data.hosted_invoice_url}
                    invoicePdf={data.invoice_pdf}
                  />  
        {userPreferenceGet() === true ?  reminderNotification(data) : console.log('{User Settings} User has chosen to not have reminders')}
        </div>
    );
  }; 

  // Shows the paid invoices when the apropriate tab is selected
  function paidCard(paidData) {

    // Converting epoch seconds to human readable date
    const invoiceCreated = moment.unix(paidData.created);
    const invoiceCreatedConverted = invoiceCreated.format(' MMMM Do YYYY');
    const invoiceDue = moment.unix(paidData.due_date);
    const invoiceDueConverted = invoiceDue.format(' MMMM Do YYYY');

    // Executing the function to convert amout due.
    const amountDue = decimalInsert(paidData.amount_due);
   
    
    // Shows a notification when data from database has been loaded
    return(
                  <Card
                    invoiceId={paidData.id}
                    invoiceCreated={invoiceCreatedConverted}
                    invoiceDue={invoiceDueConverted}
                    amountDue={amountDue}
                    invoicePdf={paidData.invoice_pdf}
                  /> 
    );
  };
  let invoiceData = []
  let paidInvoiceData = []
  // Render the invoices by mapping through state.
  if(data && paidData && Array.isArray(data) && Array.isArray(paidData)) {
     invoiceData = data.map(card);
     paidInvoiceData =  paidData.map(paidCard);
  }

 
 // If the User is still logging in then this DIV will render
  if(isLoading || !user) {
    return <div>Loading...</div>
  }  

    return (
      isAuthenticated && (
        <div className=" h-screen flex overflow-hidden "> 
          <Sidebar />
        <div className="  flex flex-col w-0 flex-1 overflow-hidden">
           {userPreferenceGet() === false ? notifcationSettings()  : console.log( '{Invoice Alert Settings} Alerts have been muted') }
          <div className="relative flex-shrink-0 flex h-16 bg-white shadow">
            <button
              type="button"
              className="px-4 border-r border-gray-200 text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500 md:hidden"
              onClick={() => setSidebarOpen(true)}
            >
              <span className="sr-only">Open sidebar</span>
              <MenuAlt2Icon className="h-6 w-6" aria-hidden="true" />
            </button>
            <div className="flex-1 px-4 flex justify-between">
              <div className="flex-1 flex">
                <form className="w-full flex md:ml-0" action="#" method="GET">
                  <label htmlFor="search-field" className="sr-only">
                    Search
                  </label>
                  <div className="relative w-full text-gray-400 focus-within:text-gray-600">
                    <div className="absolute inset-y-0 left-0 flex items-center pointer-events-none">
                      <SearchIcon className="h-5 w-5" aria-hidden="true" />
                    </div>
                    <input
                      id="search-field"
                      className="block w-full h-full pl-8 pr-3 py-2 border-transparent text-gray-900 placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-0 focus:border-transparent sm:text-sm"
                      placeholder="Search"
                      type="search"
                      name="search"
                    />
                  </div>
                </form>
              </div>
              <div className="ml-4 flex items-center md:ml-6">
                <button
                  type="button"
                  className="bg-white p-1 rounded-full text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
                >
                  <span className="sr-only">View notifications</span>
                  <BellIcon className="h-6 w-6" aria-hidden="true" />
                </button>
  
                {/* Profile dropdown */}
                <Menu as="div" className="ml-3 relative z-50">
                  <div>
                    <Menu.Button className="max-w-xs bg-white flex items-center text-sm rounded-full focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
                      <span className="sr-only">Open user menu</span>
                      <img
                        className="h-8 w-8 rounded-full"
                        src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
                        alt=""
                      />
                    </Menu.Button>
                  </div>
                  <Transition
                    as={Fragment}
                    enter="transition ease-out duration-100"
                    enterFrom="transform opacity-0 scale-95"
                    enterTo="transform opacity-100 scale-100"
                    leave="transition ease-in duration-75"
                    leaveFrom="transform opacity-100 scale-100"
                    leaveTo="transform opacity-0 scale-95"
                  >
                    <Menu.Items className="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none">
                      {userNavigation.map((item) => (
                        <Menu.Item key={item.name}>
                          {({ active }) => (
                            <a
                              href={item.href}
                              className={classNames(active ? 'bg-gray-100' : '', 'block px-4 py-2 text-sm text-gray-700')}
                            >
                              {item.name}
                            </a>
                          )}
                        </Menu.Item>
                      ))}
                    </Menu.Items>
                  </Transition>
                </Menu>
              </div>
            </div>
          </div>
  
          <main className="flex-1 relative overflow-y-auto focus:outline-none bg-gray-50">
            <div className="py-6">
              <div className="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
                <h1 className="text-2xl font-semibold text-gray-900">Dashboard</h1>
              </div>
              <div className="max-w-7xl mx-auto px-4 sm:px-6 md:px-8 flex mt-40">
                {/* Replace with your content */}
                <div className="py-4">
                  <Tabs value={value} onChange={ handleChange} aria-label="invoices tab">
                    <Tab label="Open Invoices" {...a11yProps(0)} />
                    <Tab label="Paid Invoices" {...a11yProps(1)} />
                  </Tabs>
                    <TabPanel value={value} index={0}>
                      {data  ? invoiceData : <div>Invoices could not be retrieved at this time. PLease try to refresh or contact your Administrator.</div>}
                    </TabPanel>
                    <TabPanel value={value} index={1}>
                      {paidData ? paidInvoiceData : <h1>Invoices could not be retrieved at this time. PLease try to refresh or contact your Administrator.</h1>}
                    </TabPanel>

                <Snackbar
                  id={`user-welcome-snack`}
                  place="tc"
                  color="info"
                  icon={MessageIcon}
                  message={`Welcome ${user.nickname} ???? 
                  You have ${data.length} open Invoices`}
                  open={tc}
                  closeNotification={() => setTC(false)}
                  close
                  
                  />
                <Snackbar
                  id={`fetch-from-db-snack`}
                  place="bl"
                  color="success"
                  icon={StorageIcon}
                  message="Invoices Successfully fetched from Database."
                  open={bl}
                  closeNotification={() => setBL(false)}
                  close
                 
                  />
                  <NotificationContainer/>
                </div>
                {/* /End replace */}
              </div>
            </div>
          </main>
        </div>
        </div>
      )
        
    )
}

【问题讨论】:

    标签: javascript node.js reactjs api express


    【解决方案1】:

    您在 setTimeout 中调用 fetchUserInvoices 并且在响应中您正在设置数据,这可能是原因。

    【讨论】:

    • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
    • @ajinkya 您能否进一步解释一下这种情况?使用 setTimeout() 它只应该运行一次。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-02-01
    • 2016-03-10
    • 2011-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-28
    相关资源
    最近更新 更多