Search code examples
javascriptreactjseventspromiseuse-effect

Why am I getting an empty array and incorrect data fetching in console?


I have two buttons, the movies and tvshows button. When I click on either I change the option to the opposite one as shown on the handleMovieClick and handleTVShowsClick methods. Movies and TVshows may have the same id, that is why the option is important for fetching.

I am printing to the console.

(1) array of movie/tv ids

(2) the option selected

(3) each individual object fetched based on the id and option

When I use the value to calculate the array of movie ids available the first time, I first get an empty array and then the expected result. That is the array of movie ids, option is movie and each individual promise objects is fulfilled.

Here's the kicker, when I click on the tvshows button, I am getting the following two things consoled.

(1) same movie array of ids not updated, option changes to tv, however I get a bunch of promises with "status_message: "The resource you requested could not be found" because I am basically trying to retrieve promises with movie ids, but tvshow option.

Then,

(2) Then the result is changed and I everything as expected. That is option stays the same, changed in above step, the array gets updated, and the individual promise objects are all fulfilled.

What is happening?

I get that it is happening because of the code in my JSX where I am making use of the array ids. I think I need to only call the array.map() part after the useEffect has run and the so array is updated but how to do this and why is useEffect not running the first time?

Here is the code.


const Results = () => {

    const options = ['movie', 'tv']; 
    const { value } = useParams(); // from router 
    const [ page, setPage ] = useState(1);
    const [ option, setOption ] = useState(options[0]);
    const [ array, setArray ] = useState([]);

    const handleMovieClick = () => {setOption('movie')}
    const handleTVShowClick = () => {setOption('tv')}

    useEffect(() => {
        console.log('HERE');
        fetchArrayByPage(value, page, option).then(res => setArray(res));
    }, [value, page, option])

    console.log(array);
    console.log(option);

    return (
        <div className="results-container">
            <div>
                <ul className='categories'>
                    <button onClick={handleMovieClick}>Movies<span>{0}</span></button>
                    <button onClick={handleTVShowClick}>TV Shows<span>{0}</span></button>
                </ul>
            </div>
            {
            <div>
                <div className='results-list'>
                    {array.map((arr, index) => {
                        console.log(fetchID(arr, option));
                        return <Card key={index} id={arr} option={option}></Card>})}
                </div>

                <div className='pages'>

                </div>
            </div>
            }
        </div>
    );
}

Solution

  • useEffect's callback is fired after the render and fetchArrayByPage seems to be async. So clicking on "TV shows" results in:

    1. option is changed to "tv" (array stays the same)
    2. console.log(array); console.log(option);
    3. render with new option and old array
    4. console.log('HERE'); fetchArrayByPage(...)
    5. some time passes
    6. setArray(res)
    7. render with new option and new array

    You should call fetchArrayByPage from your handle*Click handlers:

    const handleTVShowClick = () => {
        fetchArrayByPage(value, page, "tv").then(res => {
            setOption('tv');
            setArray(res);
        });
    }