0
votes

It's my first experience with React, Redux and I am totally lost. The problem is my action :

import axios from 'axios';
import { FETCH_MOVIE } from '../constants/actionTypes';
const API_KEY = <API_KEY>;
const ROOT_URL = `<API_URL>`;


export function fetchMovies(pop){

  const url = `${ROOT_URL}?api_key=${API_KEY}&sort_by=${pop}`;
  axios.get(url)
  .then(function (response) {
    console.log("response is",response)
  })
  .catch(function (error) {
    console.log(error);
  });

  return{
    type:  FETCH_MOVIE,
    payload: response.data
  };
}

Screenshot of Console.log

On Console.log it seems just fine - I can see the response has the data I need. But when I am trying to send response.data to payload it returns the error - response is not defined. What am I doing wrong?

P.s. I also tried to create const result = [] and than result = [...response.data]. The error was - SyntaxError: "result" is read-only

4
You probably have to move your return statement in the .then part - gianni
@gianni thank you for reply! I tried to moved it, the error was "Actions must be plain objects." - Etoya
Your function runs before the response is returned, so you must use the middleware as per @loelsonk answer. - gianni

4 Answers

1
votes

The const error is because, result being a variable that changes over the course of the execution, you must use 'let' and not 'const'.

Now, for the fix, response is not defined comes from the last return. A good approach would be to, instead of returning the action on this function fetchMovies, you should dispatch a new action, e.g dispatch(fetchMoviesSuccess(payload)) instead of "console.log("response is",response)", which will dispatch an action that will trigger the reducer, and , in turn, update the state of the app.

1
votes

You are performing async request using axios. You should dispatch your action using redux-thunk. Installation is easy, read more about thunk here.

Then your action should look like this:

export function fetchMovies(pop) {
  return dispatch => {
    const url = `${ROOT_URL}?api_key=${API_KEY}&sort_by=${pop}`;
    axios.get(url)
    .then(function (response) {
      console.log("response is",response);

      dispatch({
        type:  FETCH_MOVIE,
        payload: response.data
      });

    })
    .catch(function (error) {
      console.log(error);
      // You can dispatch here error 
      // Example
      dispatch({
        type:  FETCH_MOVIE_FAILED,
        payload: error
      });
    });

  }
}
1
votes

The issue with your code is that by the time you return, response is still undefined because this code run synchronously till the return statement. As you can see response is defined in console.log("response is",response) So this is where you need to do your actual magic return but in another way.

You can use redux-thunk to do these thing because this is redux async. but as I feel you are a beginner from the code I have seen, Just use the simpler way and read redux-thunk or redux-promise. if you feel your project needs this then go one.

//try to make the caller pass this.props.dispatch as param
export function fetchMovies(dispatch, pop){
  const url = `${ROOT_URL}?api_key=${API_KEY}&sort_by=${pop}`;
  axios.get(url)
  .then(function (response) {
    // only here is response define so use dispatch to triger another action (fetched data with response)
    dispatch({
        type:  FETCH_MOVIE,
        payload: response.data
    })
  })
  .catch(function (error) {
    //if you had a loader state, you call also toggle that here with erro status
    console.log(error);
  });
}

//now on the caller (onClick for instance) do this instead
fetchMovies(this.props.dispatch, pop)

As you can see from @loelsonk answer down. if you use redux-thunk then you won't need to pass dispatch from the caller redux-thunk for you. But also notice how you would return and anonymous arrow function which accept dispatch as a parameter.

1
votes

You can use redux promise middleware. I have used this in my new project. It is very simple and keeps our code and state manageable.

For every async action dispatch, it dispatches

$action_type_PENDING immediately after our action dispatch , $action_type_FULFILLED if api call success, $action_type_REJECTED if api call failure

See documentation- https://github.com/pburtchaell/redux-promise-middleware

Example from my project-

your action is

export function getQuestions() {
    return {
        type: types.GET_QUESTIONS,
        payload: axios.get('http://localhost:3001/questions')
    };
}

reducer is

const initialState = {
    isLoading: false,
    questions: []
};

const questions = (state = initialState.questions, action) => {
    switch(action.type) {
        case types.GET_QUESTIONS_FULFILLED:
            return [...action.payload.data];

        default: return state;
    }
};

For displaying loader while api call we can use following reducer

const isLoading = (state = initialState.isLoading, action) => {
    switch(action.type) {
        case (action.type.match(/_PENDING/) || {}).input:
            return true;

        case (action.type.match(/_FULFILLED/) || {}).input:
            return false;

        default: return state;
    }
};

Comment me if you need any more details on above stuff.