skip to Main Content

Example:

  <Suspense fallback={<ClearLoading />}>
    // stuff
  </Suspense>

To track if the whole suspense node finishes loading, I put an effect cleanup in <ClearLoading> like this:

const ClearLoading = () => {
  useEffect(() => {
    return () => {
      // Do sth when suspense finish
    }
  }, []);

  return null;
};

This seems stupid. What is the generally accepted way of doing this?

2

Answers


  1. Using the useEffect cleanup function in your ClearLoading component is indeed a bit unconventional for tracking when a Suspense boundary finishes loading.

    While it’s a valid approach, it can feel a bit clunky and messy. We can handle this scenario in React by following this approaches:

    1. Use a Local State with Suspense: You can maintain a local state that indicates whether the data has loaded or not. Here’s how you can do it:
        import React, { Suspense, useState } from 'react';
        
        const YourComponent = () => {
          const [loading, setLoading] = useState(true);
        
          const handleLoadingFinish = () => {
            setLoading(false);
          };
        
          return (
            <Suspense fallback={<ClearLoading onFinish={handleLoadingFinish} />}>
              {/* Your content bla bla bla */}
              {!loading && <div>Your content after loading</div>}
            </Suspense>
          );
        };
        
        const ClearLoading = ({ onFinish }) => {
          // This simulates the end of loading; you can also use a timeout or other mechanism
          useEffect(() => {
            const timer = setTimeout(() => {
              onFinish();
            }, 1000);
        
            return () => clearTimeout(timer);
          }, [onFinish]);
        
          return <div>Loading...</div>;
        };
    
    1. Custom Hook for Suspense Handling: You can create a custom hook to encapsulate the loading logic, making it reusable across different components.
        import React, { Suspense, useState, useEffect } from 'react';
        
        const useSuspenseTracking = () => {
          const [loading, setLoading] = useState(true);
        
          const onFinish = () => {
            setLoading(false);
          };
        
          return { loading, onFinish };
        };
        
        const YourComponent = () => {
          const { loading, onFinish } = useSuspenseTracking();
        
          return (
            <Suspense fallback={<ClearLoading onFinish={onFinish} />}>
              {/* Your content here same usual bla bla*/}
              {!loading && <div>Your content after loading</div>}
            </Suspense>
          );
        };
        
        const ClearLoading = ({ onFinish }) => {
          useEffect(() => {
            const timer = setTimeout(() => {
              onFinish();
            }, 1000);
        
            return () => clearTimeout(timer);
          }, [onFinish]);
        
          return <div>Loading...</div>;
        };
    
    1. Use a State Management Solution (Optional): If you’re using something like Redux or Context API, you could manage loading states globally, which can be more robust for larger applications.
    Login or Signup to reply.
  2. Using useEffect is the idiomatic approach, as mentioned in React 18 upgrade guide.

    However, by having it be an empty child component of Suspense, you wont need a wrapper for every Loading component.

    <Suspense fallback={<Loading/>}>
        <PullTheLever/>
        // stuff
    </Suspense>
    

    The tracking-component doesnt need a return value.

    function PullTheLever() {
        useEffect(() => {
            // Do sth when suspense finish
        }, []);
    
        return null;
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search