0

I'm trying to map data from the movieDB api in React & Redux and getting the error:

Uncaught TypeError: Cannot read property 'results' of undefined

For better understanding, here's my code.

reducers/moviesReducer.js

const initialState = {
  isFetchingMovies: false,
  isFetchedMovies: false,
  movieList: [],
  fetchingMoviesError: null
}

export const popularMoviesReducer = (state = initialState, action) => {
  switch (action.type) {
    case "FETCHING_POPULAR_MOVIES_START":
      return { ...state, isFetchingMovies: true, fetchingMoviesError: null }
    case "FETCHING_POPULAR_MOVIES_SUCCESS":
      return {
        ...state,
        isFetchingMovies: false,
        isFetchedMovies: true,
        movieList: action.data,
        fetchingMoviesError: null
      }
    case "FETCHING_POPULAR_MOVIES_FAILURE":
      return { ...state, fetchingMoviesError: action.data.error }
    default:
        return state
  }
}

reducers/index.js

import { combineReducers } from "redux"
import { popularMoviesReducer } from "./moviesReducer"

const rootReducer = combineReducers({
  popularMoviesReducer
})

export default rootReducer

actions/index.js

export const fetchPopularMovies = () => {
  return async dispatch => {
    dispatch({ type: "FETCHING_POPULAR_MOVIES_START" })
    try {
      const res = await axios.get(url)
      dispatch({
        type: "FETCHING_POPULAR_MOVIES_SUCCESS",
        data: { popularMovies: res.data }
      })
    } catch (err) {
      dispatch({
        type: "FETCHING_POPULAR_MOVIES_FAILURE",
        error: { error: "Something went wrong" }
      })
    }
  }
}

Components/Popular.js

import React, { Component } from "react"
import { connect } from "react-redux"
import { fetchPopularMovies } from "../actions/index"

class Popular extends Component {

  componentDidMount() {
    this.props.dispatch(fetchPopularMovies())
  }

  render() {
    const { isFetchingMovies, movieList } = this.props

    return isFetchingMovies ? (
      <p>Loading...</p>
    ) 

    :

    (

      <div>
        {
          movieList && movieList.popularMovies.results.map(movie => {
            <p>{movie.title}</p>
          })
        }
      </div>
    )
  }
}

const mapStateToProps = state => {
  return {
    isFetchingMovies: state.popularMoviesReducer.isFetchingMovies,
    movieList: state.popularMoviesReducer.movieList
  }
}

export default connect(mapStateToProps)(Popular)

I always get stuck in this kind of problem whenever I have to display some data in my Component. I need to finally know how to get rid of this!

Update: Here's the screenshot of what's in there in Popular.js in React-Dev-Tools. Screenshot

hellraiser999
  • 91
  • 2
  • 13

1 Answers1

0

Change the way you're fetching the movies:

use a function like mapDispatchToProps to dispatch actions from you component:

const mapDispatchToProps = dispatch => ({
 fetch: () => dispatch(youractions.fetchPopularMovies())
})

and in your component lifecycle hook:

componentDidMount() {
    this.props.fetch();
  }

the connect call should look like this now:

export default connect(mapStateToProps, mapDispatchToProps)(Popular)

UPDATE:

You should also return the tag inside map function and should also use a key prop (should be unique).

  <div>
   {
      movieList && movieList.popularMovies.results.map(movie => {
        return <p key={movie.title}>{movie.title}</p>
      })
    }
  </div>

Assuming title is unique, if you have ids then you can use them too.

UPDATE #2:

There's a logical error in your reducer initially isFetchingMovies in your initialState should be true not false because if it is false you will try to display the movies before they are fetched.

So change you initialState to this:

const initialState = {
  isFetchingMovies: true,
  isFetchedMovies: false,
  movieList: [],
  fetchingMoviesError: null
}

Note that the render method is called before componentDidMount. You also seem to have isFetchedMovies which changes when movies are fetched, you can use that state in your mapStateToProps and use that as a check for displaying the loading or the movies.

Ramesh Reddy
  • 10,159
  • 3
  • 17
  • 32
  • Well, what's the point in using ```mapDispatchToProps``` here? – hellraiser999 Mar 04 '20 at 06:47
  • you can pass a second argument(a function that gets the dispatch argument) to the connect call. So it basically maps functions to props that dispatch actions. – Ramesh Reddy Mar 04 '20 at 06:52
  • Read [this](https://react-redux.js.org/using-react-redux/connect-mapdispatch) – Ramesh Reddy Mar 04 '20 at 06:54
  • I think the problem here in this question is not dispatching but the array that's being returned from the api. I'm getting the data but there's some problem in displaying it properly in the component. – hellraiser999 Mar 04 '20 at 06:57
  • Update the question with the response you're getting from the API. – Ramesh Reddy Mar 04 '20 at 06:59
  • Updated my answer. – hellraiser999 Mar 04 '20 at 07:08
  • Well, I could also avoid ```isFetchingMovies``` and instead use ```if(!movieList) then show loader``` In your updated answer setting ```isFetchingMovies``` to true initially doesn't make sense. Should avoiding this bool a better choice or is there any other way? Generally, I see this pattern of declaring those Booleans in the initial reducer state, so I did that. – hellraiser999 Mar 04 '20 at 07:24
  • @hellraiser999 Using booleans is a good pattern, the approach you choose is ultimately up to you. If you ask me then I would remove the `isFetchingMovies` state from the reducer and use `isFetchedMovies`, you should make few changes accordingly. – Ramesh Reddy Mar 04 '20 at 07:39