Skip to content
Advertisement

How to properly execute logic right after setState updates the react functional component state?

Currently, I am making a profile page where it will initially fetch the user profile details from an API endpoint. I want to have a loading indicator representing the fetch status. I am now using useState hook where I store a boolean value for isLoading state. However, after reading the documentation about useState it stated that it may be asynchronous. Hence, how do I properly update isLoading to true first then execute the fetch logic? Right now here is my code snippet.

function Profile() {
  const [isLoading, setIsLoading] = useState(false);
  const [user, setUser] = useState(null);
  const { username } = useParams();

  const fetchUserDetails = async (username) => {
    setIsLoading(true);
    try {
      const userRes = await API.graphql({
        query: queries.getUser,
        variables: { username },
      });
      if (userRes.error) {
        throw new Error({ cause: userRes.error });
      }
      setUser(userRes.data.getUser);
      setIsLoading(false);
    } catch (error) {
      console.error(error);
      // push to error page
      setIsLoading(false);
    }
  };

  // Fetch user profile data
  useEffect(() => {
    fetchUserDetails(username);
  }, []);

...

Advertisement

Answer

In your example use case you can simply set isLoading to true initially and run your fetch on-mount.

function Profile() {
  const [isLoading, setIsLoading] = useState(true);
  const [user, setUser] = useState(null);
  const { username } = useParams();

  // Fetch user profile data
  useEffect(() => {
    const fetchUserDetails = async (username) => {
      try {
        const userRes = await API.graphql({
          query: queries.getUser,
          variables: { username },
        });
        if (userRes.error) {
          throw new Error({ cause: userRes.error });
        }
        setUser(userRes.data.getUser);
        setIsLoading(false);
      } catch (error) {
        console.error(error);
        // push to error page
        setIsLoading(false);
      }
    };

    fetchUserDetails(username);
  }, []);

but if you want to watch for changes to isLoading say for a reload button, you can set it false initially, set it to true in an on-mount useEffect and have your fetch useEffect dependent on isLoading

function Profile() {
  const [isLoading, setIsLoading] = useState(false);
  const [user, setUser] = useState(null);
  const { username } = useParams();

  // set isLoading to true on-mount
  useEffect(() => {
    setIsLoading(true)
  },[]);

  // Fetch user profile data
  useEffect(() => {
    const fetchUserDetails = async (username) => {
      try {
        const userRes = await API.graphql({
          query: queries.getUser,
          variables: { username },
        });
        if (userRes.error) {
          throw new Error({ cause: userRes.error });
        }
        setUser(userRes.data.getUser);
        setIsLoading(false);
      } catch (error) {
        console.error(error);
        // push to error page
        setIsLoading(false);
      }
    };

    if (isLoading) {
      fetchUserDetails(username);
    }
  }, [isLoading]);

  function triggerReload() {
    setIsLoading(true)
  }
User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement