4

I'm using FullCalander v5 for React, testing the Resource Timeline view. I'm encountering a problem with the "loading" function, the intent is to check the Fullcalendar state and in case of loading state true, display a Spinner type component (with a conditional render) instead of the Timeline, setting the Spinner component state to true with a useState. The problem is that, launching a useState from the Fullcalendar component, which is inside the render method, starts an infinite render loop. Any ideas to break the flow ?

 
 // Loading function in the container component of Fullcalendar an the useState method
 
 const [spinner, setSpinner] = useState(true);
 
 let loadingFunction = (isLoading) => {
   if (isLoading) {
     console.log("loading");
  
  setSpinner(true);
} else {
    console.log("idle");
     
setSpinner(false);
   }
 };


//  The conditional render

return (
    <>
      {spinner ? (
        <Spinner />
      ) : (
        <>
          
         <FullCalendar
         loading={loadingFunction}
         ref={calendarRef}        
         dateClick={handleEventCreate}         
         .....
         
         
ADyson
  • 57,178
  • 14
  • 51
  • 63
Paolo
  • 41
  • 3
  • Probably the issue because re-initialising fullCalendar sets off the loading event again. So don't do that. Instead just show the spinner in a modal over the top of the calendar. Don't try hiding and showing the calendar at the same time. You can make the modal full-screen and opaque / solid if you really want the calendar to disappear completely. – ADyson Mar 26 '21 at 14:31
  • Is your component a function compnenet or a class component? Where is the code that creates an infinite loop? – programmerRaj Mar 26 '21 at 14:44
  • The FullCalender component is inside another functional component (Contanier),the "loading" function of FullCalendar is the one that triggers everything. By activating a useState, it triggers a rerender of the Container component, which in turn re-render FullCalendar, which in turn is in a "loading" state again (I think it's an internal state of FCalendar), it triggers everything from scratch. The question is if there is a way to use this function with React without running into this loop, or am I setting the overall logic wrong. In pure Js with Jquery I've seen that you just do onhide() etc., – Paolo Mar 26 '21 at 15:50
  • I don't know react specifically but your logic seems wrong. You seem to respond to the loading event by potentially reloading the calendar, which in turn triggers the loading event again, which reloads the calendar. I don't know for certain but that's what it sounds like is happening. The loading event should just be to load and unload your modal... nothing else. What exactly is the "conditional render" done in response to? It unclear how it relates to the rest of your code including how you originally initialise the calendar. – ADyson Mar 26 '21 at 17:19
  • Yes that's exactly what happens. To open the Spinner cmp, I set a state in React to true. In the code above {spinner} is the conditional rendering part, where the Spinner cmp is shown if the state spinner = true, if not then render FCal component. This is generally how I use modals for other projects in React. For this reason I ask if the loading function of fullCalendar should be used in another way with React. – Paolo Mar 27 '21 at 13:31
  • Yes I've already explained the general approach - don't use it to re-render the calendar, just use it to show and hide the modal. I can't be more specific as I don't know all the complexities of react. In regular JavaScript it would be a piece of cake. – ADyson Mar 27 '21 at 13:51

2 Answers2

3

Went into the same. My solutions, (hack?) is following. Instead of letting a state variable rule the rendering of the spinner, add a css class that take it off screen. Then I took a ref to the spinner and during the loading phase in FullCalendar I call a method that removed the off-screen class, then add it again when the loading phase is over.

    /**
    * @type {{current: HTMLElement}}
    */
    const ripple = useRef(null);

    const toggleSpinner = useCallback((state) => {
        if (ripple.current) {
            if (state) {
                ripple.current.classList.remove("off-screen");
            }
            else {
                ripple.current.classList.add("off-screen");
            }
        }
    }, [ripple]);

    /*...*/

    return <>
        <div ref={ripple} className="off-screen spinner"></div>
        <FullCalendar
            loading={s => toggleSpinner(s)}

        />

    </>

All the best Staffan

ztaff
  • 243
  • 1
  • 8
0

I think this issue might be deeper than just a conditional display of the calendar.

If I have:

  const [loading, setLoading] = useState(false)
  ...

  {loading ? <Spinner /> : null}

  <FullCalendar
    plugins={[ dayGridPlugin ]}
    initialView="dayGridMonth"
    loading={e => setLoading(e)} // With this line, it continually reloads
    eventSources={[{
        url: '/api/calendar',
        method: 'GET',
      }]}
    />

It works perfectly if I comment out the loading line above, but if I want to trigger a spinner when it's loading, it puts the data load into an infinite loop.

It's like whenever anything changes, the calendar re-loads the data.

Seems like a bug (or otherwise lack of documentation: https://fullcalendar.io/docs/loading) in FullCalendar with React.

Note that it does not do this if you are only using initialEvents, but that provides less flexibility.

DreamBold
  • 2,727
  • 1
  • 9
  • 24
Ben in CA
  • 688
  • 8
  • 22