Search code examples
javascriptreactjsredux-thunkredux-toolkit

why useSelector return undefined?


I can't figure it out why it's returning undefined, when i'm using the useSelector

In console log it showing Uncaught TypeError: movies.results is undefined

error logmovieLising.js(without render movies) console.log

store.js

import {configureStore} from '@reduxjs/toolkit';
import moviesReducer from './movies/movieSlice';

export const store = configureStore({
    reducer:{
        movies:moviesReducer
    }
})

movieSlice.js

Here is the slice where i defined the initial state reducer and actions

import {createSlice,createAsyncThunk} from '@reduxjs/toolkit';
import movieApi from '../../common/api/movieApi';


const API_KEY = process.env.REACT_APP_API_KEY;


export const fetchAsyncMovies = createAsyncThunk(
    "movies/fetchAsyncMovies",
    async () => {
            const movieTerm ="harry";
            const response = await 
            movieApi.get(`/search/movie?api_key=${API_KEY}&query=${movieTerm}`);
            // console.log(response.data);
            return response.data;
    }
);



const initialState = {
    movies:{},
}

const movieSlice = createSlice({
    name:"movies",
    initialState,
    reducers:{

        addMovies:(state,{payload}) =>{
            state.movies= payload;
        }
    },
    extraReducers:{
        [fetchAsyncMovies.pending]: () => {
        console.log("Pending");
        },
        [fetchAsyncMovies.fulfilled]: (state, { payload }) => {
        console.log("Fetched Successfully!");
        return { ...state, movies: payload };
        },
        [fetchAsyncMovies.rejected]: () => {
        console.log("Rejected!");
        },
    }
});



export const {addMovies} = movieSlice.actions;

export const getAllMovies = (state) => state.movies.movies;

export default movieSlice.reducer

Home.js

This is the Home component where i use the useDispatch

import React, { useEffect } from 'react';
import MovieListing from '../MovieListing/MovieListing';
import {useDispatch} from "react-redux";
import {fetchAsyncMovies} from "../../features/movies/movieSlice"

const Home = () => {
    const dispatch = useDispatch();
    useEffect(()=>{
        dispatch(fetchAsyncMovies());
        
    },[dispatch]);
    return(
        <div>
            <div className="banner-img"></div>
            <MovieListing/>
        </div>
    ) ;
};

export default Home;

MovieListing.js(COMPONENT)

import React from 'react';
import {useSelector} from 'react-redux';
import {getAllMovies} from '../../features/movies/movieSlice';
import MovieCard from '../MovieCard/MovieCard';
import './movieListing.scss'

const MovieListing = () => {
    
    const movies = useSelector(getAllMovies);
     let renderMovies ="";
    
     renderMovies = movies.results.length > 0 ? (movies.results.map((movie,index)=>(
         <MovieCard key={index} data={movie} />
     ))) :(
        <div className="movies-error">
             <h3>{movies.Error}</h3>
         </div>
     );
console.log(movies.results);
    return (
        <div className="movie-wrapper">
            <div className="movie-list">
                <h2>Movies</h2>
                <div className="movie-container">
                    {renderMovies }
                </div>
            </div>

        </div>
    );
};

export default MovieListing;

Solution

  • Since you defined your initial state as :

    const initialState = { movies:{}, }

    In the very first render, movies.results is undefined, so trying to use the length property leads to an error.

    try defining your initial state like this:

    const initialState = { movies:{results:[]}, }
    

    another solution would be always checking if movies.results is defined, before using its properties:

    renderMovies = movies.results && movies.results.length > 0 ? ...