Zeusox
Zeusox

Reputation: 8448

React & Redux: Infinite Scroll Causes a TypeError: Dispatch is not a Function Error

I am using react-infinite-scroll-component package as a way to load data when users keep scrolling (infinite scrolling). However, when I set it all up with Redux and everything, and if I scroll to the bottom, I get the following error on my action method:

TypeError: dispatch is not a function

These are my files for replication or more context:

TopRatedComponent.js:

import React, { useState, useEffect } from "react";
import { useSelector } from "react-redux";
import { connect } from "react-redux";
import { fetchTopRatedMovies } from '../actions/topRatedAction.js';
import { Container, Row, Col, Button } from 'react-bootstrap';
import { useDispatch } from "react-redux";
import InfiniteScroll from "react-infinite-scroll-component";

const TopRatedComponent = (props) => {
    const dispatch = useDispatch();
    
    let [page, setPage] = useState(3);

    useEffect(() => {
        dispatch(fetchTopRatedMovies(page));
        setPage = page++;
    }, [])

    const { 
        topRatedMovies,
        loading,
        error
     } = useSelector(state => state.topRatedMovies);
 
    
    return (
        <div >
        {loading && <div>LOADING...</div>}
        {error && <div>{error}</div>}

        <Container className="p-4"  >
        <InfiniteScroll
          dataLength={20}
          next={fetchTopRatedMovies(page)}
          hasMore={true}
          loader={<h4>Loading...</h4>}
        >
              {topRatedMovies.results && topRatedMovies.results.map(topRated => (
            <div key={topRated.id}>
              div - #{topRated.title}
            </div>
          ))}
        </InfiniteScroll>
        </Container>

    </div>
    )
};



const mapStateToProps = state => {
    const { topRatedMovies, loading, error } = state.topRatedMovies;
    return {
        topRatedMovies,
        loading,
        error
    };
};

export default connect(
    mapStateToProps,
    {
        fetchTopRatedMovies
    }
)(TopRatedComponent);

store.js:

import { createStore, compose, applyMiddleware, combineReducers } from "redux";
import thunkMiddleware from 'redux-thunk'
import topRatedReducer from '../reducers/topRatedReducer'

  const rootReducer = combineReducers({
    topRatedMovies: topRatedReducer
  });

  const store = createStore(rootReducer,  compose(applyMiddleware(thunkMiddleware),
  ));
  
  export default store;

topRatedAction.js:

import {
  FETCH_TOPRATED_START,
  FETCH_TOPRATED_FAILURE,
  FETCH_TOPRATED_SUCCESS 
} from './actionTypes'
import axios from "axios";


const API_KEY = 'MyAPIKEY';
export  const fetchTopRatedMovies = (page) => {
   // page = 1;
    const API_URL = `https://api.themoviedb.org/3/movie/top_rated?api_key=${API_KEY}&language=en-US&page=` + page;
    return dispatch => {
        dispatch(fetchTopRatedStarted());
        axios.get(API_URL)
        .then(response => {
            dispatch(fetchTopRatedSucceeded(response.data));
        })
        .catch(error =>{
            dispatch(fetchTopRatedFailed(error.message));
        });
    };
};



const fetchTopRatedStarted = () => {
  return{
      type: FETCH_TOPRATED_START,
      payload: {
          isLoading: true
      }
  };
};

const fetchTopRatedSucceeded = topRatedMovies => {
    return{
        type: FETCH_TOPRATED_SUCCESS,
        payload: {
            topRatedMovies
        }
    };
};

const fetchTopRatedFailed = error => {
    return{
        type: FETCH_TOPRATED_FAILURE,
        payload: {
            error
        }
    };
};

topRatedReducer.js

import {
    FETCH_TOPRATED_START,
    FETCH_TOPRATED_FAILURE,
    FETCH_TOPRATED_SUCCESS 
  } from '../actions/actionTypes'

  const initialState = {
      topRatedMovies: [],
      loading: false,
      error: null
  }



  export default function (state = initialState, action){

    switch (action.type) {
        case FETCH_TOPRATED_START:
            return{
                ...state,
                loading: true
            };
        case FETCH_TOPRATED_FAILURE:
            return{
                ...state,
                loading: false,
                error: action.payload.error
            }
        case FETCH_TOPRATED_SUCCESS:
            return{
                ...state,
                loading:false,
                topRatedMovies: action.payload.topRatedMovies
            };
        default:
            return state
    }

  }

Upvotes: 0

Views: 361

Answers (2)

Shubham Khatri
Shubham Khatri

Reputation: 281764

Since you are using connect and passing fetchTopRatedMovies already to mapDispatchToProps, you can simply useed the dispatch function from props rather than calling dispatch on the imported function

Also the next props of InfiniteScroll shouldn't use fetchTopRatedMovies directly but from props since thaat will be the dispatched function instance. Your updated code will look like

const TopRatedComponent = (props) => {
    
    let [page, setPage] = useState(3);

    useEffect(() => {
        props.fetchTopRatedMovies(page);
        setPage(page => page+1); // call setPage function
    }, [])

    const { 
        topRatedMovies,
        loading,
        error
     } = props
    const fetchData = () => {
        props.fetchTopRatedMovies(page);
        setPage(page => page + 1);
    }
    
    return (
        <div >
        {loading && <div>LOADING...</div>}
        {error && <div>{error}</div>}

        <Container className="p-4"  >
        <InfiniteScroll
          dataLength={20}
          next={fetchData}
          hasMore={true}
          loader={<h4>Loading...</h4>}
        >
              {topRatedMovies.results && topRatedMovies.results.map(topRated => (
            <div key={topRated.id}>
              div - #{topRated.title}
            </div>
          ))}
        </InfiniteScroll>
        </Container>

    </div>
    )
};

const mapStateToProps = state => {
    const { topRatedMovies, loading, error } = state.topRatedMovies;
    return {
        topRatedMovies,
        loading,
        error
    };
};

export default connect(
    mapStateToProps,
    {
        fetchTopRatedMovies
    }
)(TopRatedComponent);

or

import React, { useState, useEffect } from "react";
import { useSelector } from "react-redux";
import { connect } from "react-redux";
import { fetchTopRatedMovies } from '../actions/topRatedAction.js';
import { Container, Row, Col, Button } from 'react-bootstrap';
import { useDispatch } from "react-redux";
import InfiniteScroll from "react-infinite-scroll-component";

const TopRatedComponent = (props) => {
    const dispatch = useDispatch();
    
    let [page, setPage] = useState(3);

    useEffect(() => {
        dispatch(fetchTopRatedMovies(page));
        setPage(page => page+1); // call setPage function
    }, [])

    const { 
        topRatedMovies,
        loading,
        error
     } = useSelector(state => state.topRatedMovies);
 
    const fetchData = () => {
        dispatch(fetchTopRatedMovies(page));
        setPage(page => page + 1);
    }
    return (
        <div >
        {loading && <div>LOADING...</div>}
        {error && <div>{error}</div>}

        <Container className="p-4"  >
        <InfiniteScroll
          dataLength={20}
          next={fetchData}
          hasMore={true}
          loader={<h4>Loading...</h4>}
        >
              {topRatedMovies.results && topRatedMovies.results.map(topRated => (
            <div key={topRated.id}>
              div - #{topRated.title}
            </div>
          ))}
        </InfiniteScroll>
        </Container>

    </div>
    )
};

export default TopRatedComponent; // Note that we are not using connect here

P.S. Note that you don't need to use both connect and hooks like useSelector and useDispatch, you can use either one of them. Following any one of the above approaches will work for you.

Upvotes: 1

Abhinav Srivastava
Abhinav Srivastava

Reputation: 20

const infinite = () => {
  dispatch(fetchTopRatedMovies(page));
  setPage=page++;
};

then call infinite from next

next={infinite}

Upvotes: 1

Related Questions