0

I am new to React and am having a difficult time figuring out how I can wait for the state to have a specific (not null) update before fetching data. I am using firebase JWT and am passing the token into the headers but with my current code it runs and passed the value of null. Is there a nifty hook trick to ensure that my fetchData function only runs once and that it only runs after the token value is set?

I tried setting the state as const [token, setToken] = useState(auth.currentUser.getIdToken()); but it appears to return a promise into the header and not the token (guessing its because its async). Thanks!

import React, { useState, useEffect } from 'react';

import { auth } from '../../firebase-config';

const RecordEntry = (props) => {
  
  const [token, setToken] = useState();

  const [isLoading, setIsLoading] = useState(false);

    var mydata = 
     {
        entry_id = props.entry_id
    }


  //should only call this once
  const fetchData = async () => {
      const current_token = auth.currentUser.getIdToken();
      setToken(current_token);
      //need to yield here to verify token is set and not null - this is where I am stuck
      fetch('https://mysite/api/recordEntry' , {
        method: 'POST',
        headers: new Headers({ 
          "Content-Type": "application/json",
          Authorization: `Bearer ${token}`,
        }),
        body: JSON.stringify(mydata)
        })
      .then((response) => response.json())
      .then((data) => {
        setIsLoading(false);
      })
      .catch((error) => {
        setIsLoading(false);
        console.log(error);
      });
  };


//passing empty array so the effect only runs once
  useEffect(() => {
    fetchData();
}, []);



  if (isLoading) {
    return <div>Loading...</div>;
  }
  return (
    <div>
        <h1> Entry Recorded </h1>
    </div>
  );
};
export default RecordEntry;
Shawn
  • 127
  • 1
  • 12
  • you need to create a state boolean "updated: false" with the first update you set it to true and then you let your fetch data stop working – Ibrahim shamma Jan 13 '22 at 21:48
  • Have you tried `const current_token = await auth.currentUser.getIdToken();`? Should work because you're in an async function. You could also do `await` instead of the then/catch for the rest. Just wrap it in try/catch so you can catch errors. – AWolf Jan 13 '22 at 22:32
  • @AWolf - just tried, still just returns a promise for token – Shawn Jan 13 '22 at 22:55
  • @AWolf, thank you. I was able to get it to work by passing current_token (using await as you mentioned) instead of using the state variable of token. Please post as an answer and I'll mark it accepted. – Shawn Jan 13 '22 at 22:59

3 Answers3

0

Try this solution

  const [didFetch,setDidFetch] = useState(false)
  useEffect(() => {
     if(!didFetch){
     setDidFetch(true)
     fetchData();
     }
   }, []);
Ibrahim shamma
  • 399
  • 5
  • 13
  • Thanks for the response, I attempted this solution but the token is still not updated. The header shows it's a promise object, instead of the expected token string. The token is still awaiting to update essentially. I need a method that pauses the data fetch until the token is filled. – Shawn Jan 13 '22 at 22:26
-1

"Thanks for the response, I attempted this solution but the token is still not updated. The header shows it's a promise object, instead of the expected token string. The token is still awaiting to update essentially. I need a method that pauses the data fetch until the token is filled."

So try this:

const [token, setToken] = useState(null);

And

useEffect(() => {
   if (token != null) fetchData();
}, [token]);
-1

Using @awolf's suggestion of await for current_token and then bass that as the auth bearer instead of the version updating to state. Worked perrfectly. Here is the final solution:

import React, { useState, useEffect } from 'react';

import { auth } from '../../firebase-config';

const RecordEntry = (props) => {
  
  const [token, setToken] = useState();

  const [isLoading, setIsLoading] = useState(false);

    var mydata = 
     {
        entry_id = props.entry_id
    }


  //should only call this once
  const fetchData = async () => {
      const current_token = await auth.currentUser.getIdToken();
      setToken(current_token);
      //need to yield here to verify token is set and not null - this is where I am stuck
      fetch('https://mysite/api/recordEntry' , {
        method: 'POST',
        headers: new Headers({ 
          "Content-Type": "application/json",
          Authorization: `Bearer ${current_token}`,
        }),
        body: JSON.stringify(mydata)
        })
      .then((response) => response.json())
      .then((data) => {
        setIsLoading(false);
      })
      .catch((error) => {
        setIsLoading(false);
        console.log(error);
      });
  };


//passing empty array so the effect only runs once
  useEffect(() => {
    fetchData();
}, []);



  if (isLoading) {
    return <div>Loading...</div>;
  }
  return (
    <div>
        <h1> Entry Recorded </h1>
    </div>
  );
};
export default RecordEntry;
Shawn
  • 127
  • 1
  • 12