Search code examples
reactjsreact-nativereact-hooksdropbox-apiuse-effect

Only run a useEffect fetch after first useEffect fetch has fired and setUser in context


I need to call the dropbox api twice. The first useEffect gets the names and paths and then pushs them into a blank array in context. Then I need the second useEffect to fire which relies on the arrays being populated from the first useEffect. I have attempted to useState and create loading and setLoading and then wrap the inside of the useEffect in and if(loading) but that didn't work because I am guessing it only tries to fire when the component is mounted. How do I make the second useEffect run once the first has completed?

const [user, setUser] = useContext(UserContext);

First useEffect:

useEffect(() => {
    var myHeaders = new Headers();
    myHeaders.append(
      'Authorization',
      'Bearer XXXXXXXXXXXXXXX',
    );
    myHeaders.append('Content-Type', 'application/json');

    var raw = JSON.stringify({path: `/${user.username}`});

    var requestOptions = {
      method: 'POST',
      headers: myHeaders,
      body: raw,
      redirect: 'follow',
    };

    fetch('https://api.dropboxapi.com/2/files/list_folder', requestOptions)
      .then(response => response.json())
      .then(data => {
        let tmpArray = [];
        let tmpArrayTwo = [];
        for (var i = 0; i < data.entries.length; i++) {
          tmpArray.push(data.entries[i].name);
          tmpArrayTwo.push(data.entries[i].path_lower);
        }
        setUser({
          ...user,
          imageNames: tmpArray,
          imagePaths: tmpArrayTwo,
        });
      })
      .catch(error => console.log('error', error));
  }, []);

Second useEffect:

useEffect(() => {
    let tmpArray = [];
    var myHeaders = new Headers();
    myHeaders.append(
      'Authorization',
      'Bearer XXXXXXXXXXXXXX',
    );
    myHeaders.append('Content-Type', 'application/json');

    for (var i = 0; i < user.imagePaths.length; i++) {
      var raw = JSON.stringify({path: `/${user.imagePaths[i]}`});
      var requestOptions = {
        method: 'POST',
        headers: myHeaders,
        body: raw,
        redirect: 'follow',
      };
      fetch(
        'https://api.dropboxapi.com/2/files/get_temporary_link',
        requestOptions,
      )
        .then(response => response.json())
        .then(data => {
          tmpArray.push(data.link);
        })
        .catch(error => console.log('error', error));
    }
    console.log(tmpArray);
    setUser({
      ...user,
      imageLinks: tmpArray,
    });
  }, []);

I am thinking it has something to do with the dependency array but everything I have put in there (like user, setUser) gives me an infinite loop and the console.log for the user.imagePaths keeps logging as []. I know user.imagePaths is populated after the first useEffect but the second useEffect keeps getting undefined for the user.imagePaths.length for the loop.


Solution

  • You should add user.imagePaths to the dependency list of the second useEffect.

    I would suggest using async/await and making the calls in single useEffect though.

    If the user is undefined by default then you might need to add optional chaining.

    [UPDATE]

    Try changing the second useEffect to this:

    useEffect(() => {
        var myHeaders = new Headers();
        myHeaders.append(
            'Authorization',
            'Bearer XXXXXXXXXXXXXX',
        );
        myHeaders.append('Content-Type', 'application/json');
    
        if (!user?.imagePaths) {
            return;
        }
    
        Promise.all(user.imagePaths.map(path => {
            var raw = JSON.stringify({path: `${path}`});
            var requestOptions = {
              method: 'POST',
              headers: myHeaders,
              body: raw,
              redirect: 'follow',
            };
            return fetch(
              'https://api.dropboxapi.com/2/files/get_temporary_link',
              requestOptions,
            )
              .then(response => response.json())
              .then(data => data.link)
              .catch(error => console.log('error', error));
          }),
        ).then(imageLinks => {
          setUser({
            ...user,
            imageLinks,
          });
        });
      }, [user?.imagePaths]);