C. L.
C. L.

Reputation: 189

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

Upvotes: 11

Views: 7241

Answers (9)

Praveen
Praveen

Reputation: 11

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)
);

}

Upvotes: 1

Richard Rosario
Richard Rosario

Reputation: 125

I have been reading the posts and answers, and everyone is right.. but it may be that you dont have ReduxPromise installed in your dependencies.. please check your package.json file to see, and then run or rerun "npm install redux-promise --save" .. that should fix the issue..

you should see the dependencies like this in your package.json file:

"dependencies": {
    "axios": "^0.18.0",
    "react": "16.3.2",
    "react-dom": "16.3.2",
    "react-redux": "5.0.7",
    "redux": "4.0.0",
    **"redux-promise": "^0.6.0"**

Upvotes: 0

Syarip Hidayat
Syarip Hidayat

Reputation: 1

Open your index.js (root folder):

  • Import ReduxPromise from 'redux-promise',don't forget to install it first (npm install --save redux-promise)

import ReduxPromise from 'redux-promise';

  • add ReduxPromise to your applyMiddleware statement

const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);

c. re-test your code.

Upvotes: 0

kam
kam

Reputation: 425

if you go to your main index file (not the one in actions folder but one in the src folder)..and assure you have the following lines.

  1. import ReduxPromise from 'redux-promise';)
  2. import {createStore, applyMiddleware} from 'redux';
  3. const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);
  4. Add the folowing property to Provider tag => store= {createStoreWithMiddleware(reducers)}

    Where reducers are coming from combined reducers file (./reducers).

Upvotes: 0

kam
kam

Reputation: 425

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
  };
}

Upvotes: 0

Michael
Michael

Reputation: 3739

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'));

Upvotes: 1

Sergey Lokhmachev
Sergey Lokhmachev

Reputation: 1

in main index.js

const createStoreWithMiddleware = applyMiddleware(promise)(createStore);

in actions/index.js

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

in reducers/reducer_weather.js

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

export default function(state = [], action) {
  switch (action.type) {
    case FETCH_WEATHER:
      return [ action.payLoad.data, ...state ]; 
  }
  return state;
}

Works fine

Upvotes: 0

Tiago Alves
Tiago Alves

Reputation: 2316

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.

Upvotes: 6

Chris
Chris

Reputation: 9

I was going through the same exact issue, the problem here is you did not apply the promise to your middleware in the base index.js you have. In your main index.js you need to include this line of code:

const createStoreWithMiddleware = applyMiddleware(promise)(createStore);

That will fix the issue.

Upvotes: 0

Related Questions