Mettre des fonctions Async dans le hook useEffect () dans React

Oct 04 2020

Je termine une application dans laquelle je souhaite masquer ma clé API GoogleMap derrière mon API sécurisée. Mon intention est d'appeler la clé API une fois que j'ai vérifié que j'ai un utilisateur authentifié. Le problème est qu'entre les appels asynchrones, les changements ne sont pas reflétés avec le changement d'état.

Voici ce que je fais:

export default function App() {
  const [dateRange, setDateRange] = useState(initialDateRange);
  const [formState, updateFormState] = useState(initialFormState);
  const [user, setUser] = useState(null);
  const [googleApiKey, setGoogleApiKey] = useState(null);

  useEffect(() => {
    async function updateAuth() {
      try {
        await checkUser();
        await getGoogleApiKey();
        await setAuthListener();
      } catch (error) {}
    }
    updateAuth();
  }, []);

  async function checkUser() {
    try {
      const user = await Auth.currentAuthenticatedUser();
      setUser(user);
      if (user !== authenticatedUser) {
        updateFormState(() => ({
          ...formState,
          authenticatedUser: user
        }));
      }
    } catch (error) {
      console.log(error);
    }
  }

  async function getGoogleApiKey() {
    const googleApiUrl = `${process.env.REACT_APP_API_PATH}apikey?googleapikey=true`; 
    try {
      console.log('USER_USER_USER', user);
      const apiKey = await fetch(googleApiUrl, {
        method: 'GET',
        headers: {
          'Content-Type': 'application/json',
          Authorization: user.signInUserSession.idToken.jwtToken
        }
      });
      console.log('GOT_GOOGLE_API_KEY', apiKey);
      setGoogleApiKey(apiKey);
    } catch (error) {
      console.log(error);
    }
  }

  async function setAuthListener() {
    Hub.listen('auth', (data) => {
      const event = data.payload.event;
      switch (data.payload.event) {
        case 'signOut':
          console.log('signing out...');
          console.log(event);
          updateFormState(() => ({
            ...formState,
            username: '',
            password: '',
            email: '',
            authenticatedUser: null
          }));
          break;
        default:
          break;
      }
    });
  }

Mais j'obtiens l'erreur:

USER_USER_USER null
App.js:78 TypeError: Cannot read property 'signInUserSession' of null
    at getGoogleApiKey (App.js:72)
    at updateAuth (App.js:42)

Si ce n'est pas le bon paradigme, j'apprécierais toutes les alternatives!

Réponses

4 Yuan-HaoChiang Oct 04 2020 at 08:33

Lorsqu'elle setUserest appelée, la uservariable contient toujours d'anciennes userinformations ( nulldans ce cas), indépendamment de async/await:

await checkUser();        // setUser(something) will not happen until next run
await getGoogleApiKey();  // user is still null
await setAuthListener();

Un autre choix serait d'ajouter un autre effet lors des usermodifications:

useEffect(() => {
  if (user) {
     getGoogleApiKey();
  }
}, [user]);

Ou appelez getGoogleApiKeyavec le paramètre:

const user = await checkUser(); // return user
await getGoogleApiKey(user);
await setAuthListener();

Étant donné que la logique devient complexe, je suggérerais d'essayer le hook useReducer car la répartition simplifie ces scénarios complexes de va-et-vient.