Search code examples
reactjsreact-nativeasync-awaitreact-hooksaws-amplify

Custom React-native Hook runs the component that calls it twice. I don't understand why


I am trying to learn to work with custom Hooks in React-native. I am using AWS Amplify as my backend, and it has a method to get the authenticated user's information, namely the Auth.currentUserInfo method. However, what it returns is an object and I want to make a custom Hook to both returns the part of the object that I need, and also abstract away this part of my code from the visualization part. I have a component called App, and a custom Hook called useUserId. The code for them is as follows:

The useUserId Hook:

import React, { useState, useEffect } from "react";
import { Auth } from "aws-amplify";

const getUserInfo = async () => {
  try {
    const userInfo = await Auth.currentUserInfo();
    const userId = userInfo?.attributes?.sub;
    return userId;
  } catch (e) {
    console.log("Failed to get the  AuthUserId", e);
  }
};

const useUserId = () => {
  const [id, setId] = useState("");
  const userId = getUserInfo();

  useEffect(() => {
    userId.then((userId) => {
      setId(userId);
    });
  }, [userId]);

  return id;
};

export default useUserId;

The App component:

import React from "react";
import useUserId from "../custom-hooks/UseUserId";

const App = () => {
  const authUserId = useUserId();
  console.log(authUserId);

However, when I try to run the App component, I get the same Id written on the screen twice, meaning that the App component is executed again.

The problem with this is that I am using this custom Hook in another custom Hook, let's call it useFetchData that fetches some data based on the userId, then each time this is executed that is also re-executed, which causes some problems.

I am kind of new to React, would you please guide me on what I am doing wrong here, and what is the solution to this problem. Thank you.


Solution

  • The issue is likely due to the fact that you've declared userId in the hook body. When useUserId is called in the App component it declares userId and updates state. This triggers a rerender and userId is declared again, and updates the state again, this time with the same value. The useState hook being updated to the same value a second time quits the loop.

    Bailing out of a state update

    If you update a State Hook to the same value as the current state, React will bail out without rendering the children or firing effects. (React uses the Object.is comparison algorithm.)

    Either move const userId = getUserInfo(); out of the useUserId hook

    const userId = getUserInfo();
    
    const useUserId = () => {
      const [id, setId] = useState("");
    
      useEffect(() => {
        userId.then((userId) => {
          setId(userId);
        });
      }, []);
    
      return id;
    };
    

    or more it into the useEffect callback body.

    const useUserId = () => {
      const [id, setId] = useState("");
    
      useEffect(() => {
        getUserInfo().then((userId) => {
          setId(userId);
        });
      }, []);
    
      return id;
    };
    

    and in both cases remove userId as a dependency of the useEffect hook.