jayluvsdivn
jayluvsdivn

Reputation: 35

How to fix "dispatch is not a function" error

I am working on a simple react-redux project that gets information about movies from the OMDB api based on search term provided by the user. I am currently having trouble trying to get text typed into the searchbar to update the store value corresponding to the title of the film to search for. I'm fairly new to react and completely new to redux I've only finished one other redux project before and I set up my actions and reducers in the exact same way as last time but this time I'm running into "Uncaught TypeError: dispatch is not a function". This was not a problem I encountered in the previous project and my google searching has not been very helpful thus far.

I've searched this problem on google and only found a few results and none of them seem to be having the exact same issue as me, they involve using mapDispatchToProps which I'm not using inside of my connect function. Supposedly when you write a mapStateToProps like I have, dispatch should just be passed down as a prop to the connected component but whenever I try to access it I get the aforementioned "Uncaught TypeError: dispatch is not a function" error.

here is the index.js for my component

import { connect } from 'react-redux';
import MovieSearch from './MovieSearchContainer';
import { 
  updateSearchTerm,
  getMovies
} from './movieSearchActions';

function mapStateToProps(state){
  return {
    title: state.movieSearch.title,
    year: state.movieSearch.year,
    plot: state.movieSearch.plot,
    released: state.movieSearch.released,
    runtime: state.movieSearch.runtime,
    genre: state.movieSearch.genre,
    plot: state.movieSearch.plot,
    ratings: {
      IMDB: state.movieSearch.ratings.IMDB,
      Metascore: state.movieSearch.ratings.Metascore
    },
    posterUrl: state.movieSearch.posterUrl,
    cachedMovies: state.movieSearch.cachedMovies
  };
}

export default connect(mapStateToProps)(MovieSearch);

here is my action

export function updateSearchTerm(searchTerm){
  return {
    type: "UPDATE_SEARCH_TERM",
    payload: { searchTerm }
  }
}

here is my jsx component

import React from 'react';
import PropTypes from 'prop-types';
import {
  updateSearchTerm,
  getMovies
} from './movieSearchActions';

export default class MovieSearchContainer extends React.Component 
{
  constructor(props) {
    super(props);

    this.handleUpdateSearchTerm = 
    this.handleUpdateSearchTerm.bind(this);
  }

  handleUpdateSearchTerm(event){
    const { dispatch } = this.props;
    const { value } = event.target;
    dispatch(updateSearchTerm(value));
  }

  render() {
    return (
      <div>
        <h1 className='text-center'>Movie Finder</h1>
        <input type='text' className='col-sm-11' id='searchBar' 
        onChange={ this.handleUpdateSearchTerm }/>
        <button type='button' id='getMovies' className='col-sm- 
        1'>Go!</button>
      </div>
    )
  }
}

MovieSearchContainer.propTypes = {
  store: PropTypes.object
}

here is the reducer

export default function movieSearchReducer(state = defaultState, 
action) {
  const { type, payload } = action;

  switch(type){
    case 'UPDATE_SEARCH_TERM': {
      return {
        ...state,
        title: payload.title
      }
    }

    default: {
      return state;
    }
  }
}

I expect changes in the searchbar on the component on the page to be reflected in the redux store, but instead I just get this error

Upvotes: 3

Views: 9393

Answers (2)

Dostonbek Oripjonov
Dostonbek Oripjonov

Reputation: 1674

I think you should connect your component inside your jsx file. Then you can access with this.props.yourFunctionToDispatch

import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import {
  updateSearchTerm,
  getMovies
} from './movieSearchActions';

class MovieSearchContainer extends React.Component 
{
  constructor(props) {
    super(props);

    this.handleUpdateSearchTerm = 
    this.handleUpdateSearchTerm.bind(this);
  }

  handleUpdateSearchTerm(event){
    const { dispatch } = this.props;
    const { value } = event.target;
    dispatch(updateSearchTerm(value));
  }

  render() {
    return (
      <div>
        <h1 className='text-center'>Movie Finder</h1>
        <input type='text' className='col-sm-11' id='searchBar' 
        onChange={ this.handleUpdateSearchTerm }/>
        <button type='button' id='getMovies' className='col-sm- 
        1'>Go!</button>
      </div>
    )
  }
}
const mapStateToProps = state => {
  return {
    title: state.movieSearch.title,
    year: state.movieSearch.year,
    plot: state.movieSearch.plot,
    released: state.movieSearch.released,
    runtime: state.movieSearch.runtime,
    genre: state.movieSearch.genre,
    plot: state.movieSearch.plot,
    ratings: {
      IMDB: state.movieSearch.ratings.IMDB,
      Metascore: state.movieSearch.ratings.Metascore
    },
    posterUrl: state.movieSearch.posterUrl,
    cachedMovies: state.movieSearch.cachedMovies
  };
}

MovieSearchContainer.propTypes = {
  store: PropTypes.object
}
export default connect(mapStateToProps, {yourFunctionToDispatch})(MovieSearchContainer);

Upvotes: 0

Cat_Enthusiast
Cat_Enthusiast

Reputation: 15708

The dispatch prop is only available when you are directly interacting with the redux-store. When you define something like mapDispatchToProps() and pass it as the 2nd argument to connect(), dispatch, gets passed to mapDispatchToProps().

const mapDispatchToProps = (dispatch) => {
    return{
      actionCreator: (arg) => {
         dispatch(actionCreator(arg))
      }
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(Component)

If you dont want to define mapDispatchToProps(), you can effectively bind your action-creators by passing in an object to connect() as the 2nd argument. This implicitly binds dispatch to the action-creators:

import React from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { updateSearchTerm, getMovies } from "./movieSearchActions";

class MovieSearchContainer extends React.Component {
  constructor(props) {
    super(props);

    this.handleUpdateSearchTerm = this.handleUpdateSearchTerm.bind(this);
  }

  handleUpdateSearchTerm(event) {
    const { value } = event.target;
    this.props.updateSearchTerm(value);
  }

  render() {
    console.log(this.props.movies);
    return (
      <div>
        <h1 className="text-center">Movie Finder</h1>
        <input
          type="text"
          className="col-sm-11"
          id="searchBar"
          onChange={this.handleUpdateSearchTerm}
        />
        <button
          type="button"
          id="getMovies"
          className="col-sm- 
        1"
        >
          Go!
        </button>
      </div>
    );
  }
}

MovieSearchContainer.propTypes = {
  store: PropTypes.object
};

const mapStateToProps = state => {
   return {
     title: state.movieSearch.title,
     year: state.movieSearch.year,
     plot: state.movieSearch.plot,
     released: state.movieSearch.released,
     runtime: state.movieSearch.runtime,
     genre: state.movieSearch.genre,
     plot: state.movieSearch.plot,
     ratings: {
     IMDB: state.movieSearch.ratings.IMDB,
       Metascore: state.movieSearch.ratings.Metascore
     },
     posterUrl: state.movieSearch.posterUrl,
     cachedMovies: state.movieSearch.cachedMovies
   };
 };


export default connect(
  mapStateToProps,
  {
    updateSearchTerm,
    getMovies
  }
)(MovieSearchContainer);

With that, you do not need to explicitly call dispatch to use your action-creator. Simply use this.props.nameOfActionCreator()

See sandbox for example: https://codesandbox.io/s/simple-redux-7s1c0

Upvotes: 3

Related Questions