-2

useEffect keeps returning null data. Here's the code in my TypeScript app:

import TracksListing from './TracksListing';
import axios, { AxiosResponse } from 'axios'
import { useEffect, useState } from "react";

interface AlbumDetailsProps {
    id?: string
}

const AlbumDetails: React.FC<AlbumDetailsProps> = ({ id }) => {

    const [albums, setAlbums] = useState<AxiosResponse | null | any | void>([]);

    useEffect(() => {

        const fetchAlbum = async () => {
            try {
                const albumData = await axios.get(`http://localhost:8080/api/v1/album/${id}`);
                setAlbums(albumData.data);
                console.log(albums)
                console.log("this is the ID:", id)
            } catch (error) {
                console.log(error);
              }
        }

        fetchAlbum();

    }, [])

return (
                    <>
                    {albums && albums.map((album: any) => ( 
                        album.tracks.trackTitles.map((title: string, index: number) => (
                            <TracksListing number={index + 1} title={title} artist={album.artist} length={album.tracks.trackLengths[index]} />
                        ))
                    ))}
                    </>
)

If I go to localhost:8080 with any IDs in that address then the data displays there, so I know it can be read for my webpage if I could figure out what the issue is. I'm also getting "Uncaught TypeError: albums.map is not a function" errors that I assume are due to the mapping of the data not working due to the data being null.

What confuses me is I had it semi-working earlier so that when I'd load the page and nothing displays, then go to VS Code and press Ctrl+S, suddenly it'd show the data was obtained, although it still wouldn't render the data on to the page.

I've tried setting the useEffect dependency to the albums state variable or the id and it doesn't help. I have this exact same useEffect to fetch a different set of data in another component and for some reason, it works there. I don't get what makes this case different.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • _"albums.map is not a function"_ does **not** tell you it's null. – jonrsharpe Mar 02 '23 at 09:30
  • *"I'm also getting "Uncaught TypeError: albums.map is not a function" errors that I assume are due to the mapping of the data not working due to the data being null."* No, you've handled that by doing the `albums &&` part (and the error would be *"Cannot read properties of null"* or similar). You're getting that error because the `albumData.data` you're getting from axios is not an array. Put a breakpoint on the `setAlbums` statement and/or look in the Network tab to see what it actually is, so you can fix it. – T.J. Crowder Mar 02 '23 at 09:30
  • I recommend that you don't *assume* the causes of errors. Instead, [debug them](https://stackoverflow.com/questions/25385173/). [More here](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/). – T.J. Crowder Mar 02 '23 at 09:32
  • I was assuming because I had previously had the albums state set to "null" and kept seeing that in my console. I notice now that was a mistake. I'm still a newbie so get easily confused like that. – CakeoftheCup Mar 02 '23 at 10:04
  • I figured, that's why I mentioned it. :-) We were all new once! (And hopefully, throughout our lives, we're new repeatedly in various different areas.) Happy coding! :-) – T.J. Crowder Mar 02 '23 at 10:07
  • Haven't quite figured out the solution yet! :P Well, I got it working so that there's no more TypeErrors and it does indeed log a json object, so I know what I'm dealing with. But it first registers the state as null, then I guess it does a re-render because it posts the json data next. Nothing renders on my page reading from the json it seems, so I assume it only gets to reading the first null attempt and misses the second render where there's actually something in there. Got any ideas? – CakeoftheCup Mar 02 '23 at 10:59
  • @CakeoftheCup - The code above won't start out with `albums` being `null`, it will start out with `albums` being `[]` (the initializer provided to `useState`). The only way the value will change is a call to `setAlbums`. The only place you have one is where you have `setAlbums(albumData.data);`. So, again, put a breakpoint on that and look at what `albumData.data` is. Apparently it's not an array, so you need not to treat it like an array (by calling `map` on it). *(Side note: Don't stumble around in the dark with a `console.log` torch. **Turn on the lights** using the debugger. :-) )* – T.J. Crowder Mar 02 '23 at 11:02
  • Well, I just did that.. I think? Went to Source, ctrl+p'd to my component, set a breakpoint there as you said. It definitely seems to be getting an object, starts with curly braces and all. I'm just confused why doing a .map over it doesn't work, because I literally do that in another component and it gives zero issues at all. – CakeoftheCup Mar 02 '23 at 11:18
  • @CakeoftheCup - Because non-array objects don't have a `map` function. If it's an object but you want to do a map-like operation on it, [see this question](https://stackoverflow.com/questions/684672/how-do-i-loop-through-or-enumerate-a-javascript-object). But note that order is problematic (depends on how the object was created). – T.J. Crowder Mar 02 '23 at 11:20
  • OK I think it's another newbie forgetfulness thing. I just remembered I call all albums in that other function which is why puts them all into an array, so that explains that... Guess I'm going to have to find something that makes sense to me to iterate over the object. Thanks for the patience so far, this just gets so frustrating haha – CakeoftheCup Mar 02 '23 at 11:21

1 Answers1

0

Got it solved.

So the useEffect seemed ok, but the data wasn't being read as a JSON file correctly. I just made some small modifications to the .map statement as follows:

                {albums && (albums.tracks && albums.tracks.trackTitles.map((title: string, index: number) => (
                    <TracksListing number={index + 1} title={title} artist={albums.artist} length={albums.tracks.trackLengths[index]} />
                    ))
                )}

Just simply went straight for the part of the object that was an array and that let me get away with getting the data from there and elsewhere without needing to resort to any key object stuff that honestly looks too confusing for my newbie brain at the moment.

I hope somehow this helps someone else in the future, however unlikely you stumble upon this thread :)