Search code examples
reactjsreact-hooksuse-effectpreact

PReact useEffect doesn't trigger on state change


I want to do a recursive function which basically runs each time a folder has a subfolder under it, so I can take all the content from all the subfolders available.

Not sure what I am missing here, but the state change of subFolders does not trigger the useEffect which have it as dependency:

    const [imageList, setImageList] = useState([]) as any;
    const [subFolders, setSubFolders] = useState([]) as any;

    const getFilesFromFolder = (fileId: string) => {
        let noToken = false;
        
        const requestFunction = ((pageToken?: string) => {
            gapi.client.drive.files.list({
                q: `'${fileId}' in parents`,
                pageToken
            }).execute((res: any) => {    
                const token = res.nextPageToken && res.nextPageToken || null;

                const images = res.files.filter((file: any ) => 
                file.mimeType === 'image/jpeg' ||
                file.mimeType === 'image/png' ||
                file.mimeType === 'image/jpg'
                );
                
                setSubFolders([...subFolders, ...res.files.filter((file: any ) => file.mimeType === 'application/vnd.google-apps.folder')]);
                setImageList([...imageList, ...images])

                if (token) {    
                    requestFunction(token);
                } else {
                    noToken = true;
                }
            }).catch((err: any) => {
                console.log('err', err)
            })
        });

        if (!noToken) {            
            requestFunction();
        }
    }

    useEffect(() => {
        if (subFolders && subFolders.length > 0) {
            subFolders.forEach((subFolder: any) => {
                getFilesFromFolder(subFolder.id);
            });
        }
    }, [subFolders])

Solution

  • Since you are basically looping over file structure and enqueueing state updates I'm going to just assume any issues you have are because you are using normal state updates. When these are enqueued in loops or multiple times within a render cycle they overwrite the previous enqueued update.

    To resolve you should really use functional state updates. This is so each update correctly updates from the previous state, and not the state from the previous render cycle. It's a subtle but important difference and oft overlooked issue.

    Functional Updates

    setSubFolders(subFolders => [
      ...subFolders,
      ...res.files.filter((file: any ) =>
        file.mimeType === 'application/vnd.google-apps.folder'
      ),
    ]);
    setImageList(imageList => [...imageList, ...images]);