Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot access action.payLoad.data from redux promise

I am working on a weather app using redux promise, and having trouble retrieve action.payLoad.data.

actions/index.js

import axios from 'axios';

const API_KEY = 'xxxxxxxxx';
const ROOT_URL = `http://api.openweathermap.org/data/2.5/forecast?appid=${API_KEY}`;

export const FETCH_WEATHER = 'FETCH_WEATHER';

export function fetchWeather(city) {
  const url = `${ROOT_URL}&q=${city},us`;
  const request = axios.get(url);

  //console.log("request:", request);

  return {
    type: FETCH_WEATHER,
    payLoad: request
  };
}

reducers/reducer_weather.js

import  { FETCH_WEATHER } from '../actions/index';

export default function(state = [], action) {
  if (action.type === FETCH_WEATHER) {
    console.log('Action.payLoad.data received:', action.payLoad.data);
    console.log('Action received:', action);
  }

  switch (action.type) {
    case FETCH_WEATHER:
      return [ action.payLoad.data, ...state ]; // return new instance of state array
  }
  return state;
}

No matter how I tried, any combination around action.payload.data returns undefined. How should I access the its value?

screenshot

like image 940
C. L. Avatar asked Feb 03 '17 03:02

C. L.


3 Answers

Edit: I just saw the comment where you say you are using redux-promise. You should use payload in the return action, not payLoad. So your function would look like this:

function fetchWeatherPromise (city) {
  const url = `${ROOT_URL}&q=${city},us`;
  return {
      type: 'FETCH_WEATHER',
      payload: new Promise((resolve, reject) => {
          axios.get(url).then(response => resolve(response.data))
      })
    } 
};

And your reducer:

switch (action.type) {
    case 'FETCH_WEATHER_PENDING':
        ...

    case 'FETCH_WEATHER_FULFILLED':
        return [action.payload, ...state];

    default:
        return state;
};

As this very good explanation on this reddit post says:

First, some premises (in redux):

  • At the end of the redux lifecycle a new Store is returned from a reducer.
  • The new store that is returned is the end product of applying Actions to the previous store (using dispatch() !).
  • Actions should be SYNCHRONOUS in nature. The way I interpret this is that an action should be a plain object with plain data.

So if we follow this line of thought we can think of actions as the information needed by a reducer to transform the current store into a new store (new state).

I think, up to this point, you get that. So then how do you deal with api/xhr requests which are inherently asynchronous? You don't want to mix async functions with synchronous objects (actions) so then how is it possible to use api calls?

This is where action creators come in.

An Action Creator is simply a function that returns an action. Take note that this does NOT mean that an action creator has to dispatch the action it returns. All it does is return that synchronous, plain object, that we defined as an action.

Knowing this we can speculate, "Hmm, well if it's just a function that returns an object, and it's not necessarily dispatching actions, then can action creators be asynchronous?"

The answer to that question is yes!

What the action creator does is:

  • It makes an async API call using a promise.
  • It creates an object with the returned response (it might be the real data so you have to resolve your promise!).
  • It dispatchs a sync action to the store with this object.

The promise middleware removes the need to write myPromise.then()... (resolve your promise) each time you want to make an async action. Instead of resolving the promises all the time, you just give it the information it needs to build the API call and the middleware will take care of the rest.


Old: You should reshape your function and add a Middleware to deal with async action creators. Also, axios.get() returns a promise and you should dispatch a sync action inside then so your reducer has access to the data fetched. Here is an example that how your action creator should look like using redux-thunk.

function fetchWeather(city) {
return (dispatch) => {
    const url = `${ROOT_URL}&q=${city},us`;
    return axios.get(url)
        .then(res => res.data)
        .then(data => {
            dispatch({
                type: FETCH_WEATHER,
                payLoad: data
            });
        }) 
     }
}

Don't forget to add applyMiddleware(thunkMiddleware) to your store.

like image 89
Tiago Alves Avatar answered Nov 11 '22 20:11

Tiago Alves


I think you are missing to configure your redux-promise as middleware. You should just try to add redux-promise as middleware in your store configuration. refer below:

 import { applyMiddleware, combineReducers, compose, createStore } from 'redux';
 import thunk from 'redux-thunk';
 import { routerReducer, routerMiddleware } from 'react-router-redux';
 import * as Counter from './Counter';
 import * as WeatherForecasts from './WeatherForecasts';
 import BooksReducer from '../reducers/reducer_books';
 import ActiveBook from '../reducers/reducer_active_book';
 import weatherReducer from '../reducers/weather';
 import Promise from 'redux-promise';


 export default function configureStore(history, initialState) 
 {
    const reducers = 
    {
        counter: Counter.reducer,
        weatherForecasts: WeatherForecasts.reducer,
        books: BooksReducer,
        activeBook: ActiveBook,
        weather: weatherReducer
    };

const middleware = [
    thunk,
    routerMiddleware(history),
    Promise
];

// In development, use the browser's Redux dev tools extension if installed
const enhancers = [];
const isDevelopment = process.env.NODE_ENV === 'development';
if (isDevelopment && typeof window !== 'undefined' && window.devToolsExtension) {
    enhancers.push(window.devToolsExtension());
}

const rootReducer = combineReducers({
    ...reducers,
    routing: routerReducer
});

return createStore(
    rootReducer,
    initialState,
    compose(applyMiddleware(...middleware), ...enhancers)
);

}

like image 43
Praveen Avatar answered Nov 11 '22 19:11

Praveen


Check your index.js, make sure the ReduxPromise is wired in applyMiddleWare() call, like this

const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';

import App from './components/app';
import reducers from './reducers';
import ReduxPromise from 'redux-promise';

const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);

ReactDOM.render(
  <Provider store={createStoreWithMiddleware(reducers)}>
    <App />
  </Provider>
  , document.querySelector('.container'));
like image 45
Michael Avatar answered Nov 11 '22 20:11

Michael