skip to Main Content

I have three components. A parent, a child and a modal component residing inside the child. The modal must show some data fetched from the API (called fetchMyDetails) and the function resides in the parent. Whenever a button is pressed inside the child, a callback to fetchMyDetails is called and data should be passed from the parent to the modal. While the data is fetched, a loading prompt appears until the API call is completed.

The problem:

The await in handleButtonPress method is ignored and the modal is being opened before the data has completed fetching. Why is that?

My parent component:

const MyParent= () => {

  // Date pentru solicitare in curs (nota, absenta stearsa sau modificata etc)
  const [myDetails, setMyDetails] = useState<MyDetailsType | null>(null)

  const fetchMyDetails = async () => {

    dispatch((setGlobalLoadingModal(true))); // This is a global modal for loading prompts residing in App.tsx

    myFetchFunction()
      .then((response: MyDetailsType) => {

        if (response.value.length > 0) {
          setMyDetails(response.value)
        }
      })
      .catch(async (error: ApiError) => {
        // ...some error catching
        }
      })
      .finally(() => {
        dispatch((setIsModalLoadingShown(false)));
      })
  }

 return (
 <MyChild myDetails={myDetails} fetchMyDetails={fetchMyDetails}
 )

And then I have my child component

const MyChilds= ({fetchMyDetails, myDetails}: Props) => {
    
      const [showMyModal, setShowMyModal=useState<boolean>(false);


      const handleButtonPress = async () = > {
       // **THE PROBLEM IS HERE**
       await fetchMyDetails();
       setShowMyModal(true)
      }
    
     return (
     <>
     {showMyModal && 
     <MyModal myDetails={myDetails}/>
     }
     <Button onPress={handleButtonPress}/>
     </>
     )
 

3

Answers


  1. You need to make your callback an async function, like so:

    const handleButtonPress = async () = > {
      await fetchMyDetails();
      setShowMyModal(true)
    }
    
    Login or Signup to reply.
  2. const MyChild = ({ fetchMyDetails, myDetails }: Props) => {
      const [showMyModal, setShowMyModal] = useState(false);
      const [isLoading, setIsLoading] = useState(false);
    
      const handleButtonPress = async () => {
        setIsLoading(true); //Set loading state
        try {
          await fetchMyDetails();
          setShowMyModal(true);
        } catch (error) {
          // Handle error appropriately
        } finally {
          setIsLoading(false); //Turn off loading state, whether successful or not
        }
      };
    
      return (
        <>
          {isLoading && <LoadingPrompt />} {/*Show loading prompt while loading*/}
          {showMyModal && <MyModal myDetails={myDetails} />}
          <Button onPress={handleButtonPress} />
        </>
      );
    };
    Login or Signup to reply.
  3. I think the issue is that you are not awaiting myFetchFunction, the then clause is triggered after the promise is resolved, but fetchMyDetails returns before that:

    const fetchMyDetails = async () => {
      dispatch((setGlobalLoadingModal(true))); // This is a global modal for loading prompts residing in App.tsx
    
      try {
        const response: MyDetailsType = await myFetchFunction();
    
        if (response.value.length > 0) {
          setMyDetails(response.value);
        }
      } catch (error: ApiError) {
        // ...some error catching
      } finally {
        dispatch((setIsModalLoadingShown(false)));
      }
    }
    

    You can use try/catch like this. or just add await before myFetchFunction();

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search