Search code examples
reactjstypescriptreduxredux-thunkstate-management

React/TypeScript/Redux/Thunk action not getting dispatched and state not updated


I am trying to make a GET request to an API using React-Redux & TypeScript by trying to dispatch an action that makes the request when I click a button (onClick event) and then I want to update the state using the reducer and then console.log the updated state, however I can only seem to get the state that is the initialized state in the store to appear in the console, I'm not too sure what's wrong but it seems like my action is not even getting dispatched to my reducer (my console.log in my action is not triggered it appears nor in my reducer switch statement), here are the files wherein lies my the error in logic:

EDIT: The action is triggered, the console.log I wrote in below //EDIT appears in the console, but for some reason it isn't dispatched to my reducer...

src/actions/movieActions.ts:

import { ActionCreator, Dispatch } from 'redux';
import { ThunkAction } from 'redux-thunk';
import { IMovieState } from '../reducers/movieReducer';
import axios from 'axios';

export enum MovieActionTypes {
    ANY = 'ANY',
    GENRE = 'GENRE',
}

export interface IMovieGenreAction {
    type: MovieActionTypes.GENRE;
    property: Array<String>;
}

export type MovieActions = IMovieGenreAction;

/*<Promise<Return Type>, State Interface, Type of Param, Type of Action> */
export const movieAction: ActionCreator<ThunkAction<Promise<any>, IMovieState, any, IMovieGenreAction>> = () => {
  //EDIT
  console.log('movieAction Triggered')
  return async (dispatch: Dispatch) => {
    try {
      dispatch({
        type: MovieActionTypes.GENRE
      })
      console.log('moveActions called')
      const res = await axios.get(`https://api.themoviedb.org/3/genre/movie/list?api_key=${process.env.REACT_APP_MOVIE_API_KEY}&language=en-US`)
      dispatch({
        property: res,
        type: MovieActionTypes.GENRE
    })
  } catch (err) {
    console.log(err);
  }
} 
};

src/reducers/movieReducer.ts:

import { Reducer } from 'redux';
import { MovieActionTypes, MovieActions } from '../actions/movieActions';

export interface IMovieState {
    property: any;
    genres: any;
}

const initialMovieState: IMovieState = {
    property: null,
    genres: 'askksakd'
};

export const movieReducer: Reducer<IMovieState, MovieActions> = (
    state = initialMovieState,
    action
    ) => {
      switch (action.type) {
        case MovieActionTypes.GENRE: {
          console.log('MOVE ACTION CALLED')
          return {
            ...state,
            genres: action.property
          };
        }
        default:
          console.log('default action called')
          return state;
      }
  };

src/store/store.ts:

import { applyMiddleware, combineReducers, createStore, Store } from 'redux';
import thunk from 'redux-thunk';
import { IMovieState, movieReducer } from '../reducers/movieReducer';

// Create an interface for the application state
export interface IAppState {
  movieState: IMovieState
}

// Create the root reducer
const rootReducer = combineReducers<IAppState>({
  movieState: movieReducer
});

// Create a configure store function of type `IAppState`
export default function configureStore(): Store<IAppState, any> {
  const store = createStore(rootReducer, undefined, applyMiddleware(thunk));
  return store;
}

src/components/MovieForm.tsx (the file that is supposed to dispatch the action):

import React, { useState } from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Paper from '@material-ui/core/Paper';
import Box from '@material-ui/core/Box';
import Select from '@material-ui/core/Select';
import MenuItem from '@material-ui/core/MenuItem';
import { spacing } from '@material-ui/system';
import Card from '@material-ui/core/Card';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
import { CardHeader, TextField, CircularProgress } from '@material-ui/core';
import { useDispatch, useSelector } from 'react-redux';
import { movieAction } from '../actions/movieActions';
import { IAppState } from '../store/store';
import axios from 'axios';


const MovieForm = () => {

  const dispatch = useDispatch()
  const getGenres = () => {
    console.log('actions dispatched')
    dispatch(movieAction)
  }

  const genres = useSelector((state: IAppState) => state.movieState.genres);

  //const [genreChoice, setGenreChoice] = useState('')

  return (
    <>
    <h1>Movie Suggester</h1>
    <Paper elevation={3}>
      <Box p={10}>
        <Card>
          <div>Hello World. </div>
          <Select onChange={() => console.log(genres)}>
            <MenuItem>
              meow
            </MenuItem>
            <br />
            <br />
          </Select>
          <Button onClick={() => {
            getGenres()
            setTimeout(function(){
              console.log(genres)
            }, 5000)
          }}>genres list</Button>
          <Button onClick={() => console.log(axios.get(`https://api.themoviedb.org/3/discover/movie?api_key=${process.env.REACT_APP_MOVIE_API_KEY}&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&with_genres=35&page=1`))}>Click me</Button>
        </Card>
      </Box>
    </Paper>
    </>
  )
}


export default MovieForm

and here is the src/index.tsx in case the problem is here and I'm unaware:

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'
import { BrowserRouter as Router } from 'react-router-dom';
import './index.css';
import CssBaseline from '@material-ui/core/CssBaseline';
import { ThemeProvider } from '@material-ui/core/styles';
import theme from './theme';
import App from './App';
import configureStore from './store/store';

const store = configureStore();

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <ThemeProvider theme={theme}>
      {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
      <CssBaseline />
        <Router>
          <App />
        </Router>
      </ThemeProvider>
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

Thanks for taking a look at this and and attempting to help me see what I am unable to!


Solution

  • Do dispatch(movieAction()) because movieAction is a function that creates the action so you need to call it and dispatch the resulting action.