Action does not trigger a reducer in Redux

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP



Action does not trigger a reducer in Redux



I'm new to redux and trying to fetch content using the Contentful API. For some reason the action I call does not reach the reducer. I have attached the code I feel is relevant and any contributions would be highly appreciated.



actions/index.js


import axios from 'axios';

const API_BASE_URL = 'https://cdn.contentful.com';
const API_SPACE_ID = 'xxxxxxxxxxxxx';
const API_KEY ='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';

const FETCH_CONTENT = 'fetch_content';

export function fetchContent()
const request = axios.get(`$API_BASE_URL/spaces/$API_SPACE_ID/environments/master/entries?access_token=$API_KEY`);
return
type: FETCH_CONTENT,
payload: request
;



reducers/index.js


import combineReducers from 'redux';
import ContentReducer from './reducer-content';

const rootReducer = combineReducers(
contents: ContentReducer
);

export default rootReducer;



reducer-content.js


import FETCH_CONTENT from '../actions';
const INITIAL_STATE = all: ;

export default function(state = INITIAL_STATE, action)
switch(action.type)
case FETCH_CONTENT:
return ...state, all: action.payload.data.items ;

default:
return state;




index.js


import React from 'react';
import ReactDOM from 'react-dom';
import Provider from 'react-redux';
import createStore, applyMiddleware from 'redux';
import BrowserRouter, Route, Switch from "react-router-dom";
import promise from 'redux-promise';
import logger from 'redux-logger'


import ContentIndex from './components/content-index';
import reducers from './reducers';

const createStoreWithMiddleware = applyMiddleware(promise, logger)(createStore);

ReactDOM.render(
<Provider store=createStoreWithMiddleware(reducers)>
<BrowserRouter>
<div>
<Route path = "/" component = ContentIndex/>
</div>
</BrowserRouter>

</Provider>
, document.querySelector('.container'));



components/content-index.js


import React, Component from 'react';
import fetchContent from '../actions';
import connect from 'react-redux';
import _ from 'lodash';

class ContentIndex extends Component
componentDidMount()
this.props.fetchContent();


renderContent(props)
return this.props.contents.map((content, index) =>
return (
<article key=content.sys.id>
<h3>content.fields.name</h3>
<p>content.fields.website</p>
</article>
);
);


render()
return(
<div>
<h3>Content</h3>
this.renderContent()
</div>
);



const mapStateToProps = (state) =>
return contents: state.contents.all;

// export default CharacterIndex;
export default connect(mapStateToProps, fetchContent)(ContentIndex);





Like devserkan said, you need an async action, because axios always returns promises, not the data of the response directly. An async function is the easiest way to handle that, but you need to have the right middleware in redux.
– Garrett Motzner
Aug 10 at 15:38





It seems that redux-promise waits a promise, I guessed that wrong. So, when you put a console.log in your action creator and your reducer, where it fails? I mean where is it stopped being logged?
– devserkan
Aug 10 at 15:52


redux-promise


console.log




3 Answers
3



Update



It seems that I'm wrong here (Thanks to @Dave Newton's comments). redux-promise waits a promise and if it receives one, resolves it and dispatches the value. So, using an async function and using an action creator here is useless.


redux-promise



You are using redux-promise, I don't know how it handles the situation but in its Github repo there is an example with redux-actions and it uses an async function. I'm more familiar with redux-thunk but probably it suits in your situation to use an async action creator here.


redux-promise


redux-actions


redux-thunk



Try this:


export async function fetchContent()
const request = await axios.get(`$API_BASE_URL/spaces/$API_SPACE_ID/environments/master/entries?access_token=$API_KEY`);
return
type: FETCH_CONTENT,
payload: request
;





In redux-promise it expects the payload to be the promise; when the promise resolves the reducer is called with the resolved promise (but not if it rejects). Here you're just creating a normal action, which is fine, but makes redux-promise pointless.
– Dave Newton
Aug 10 at 15:38






@DaveNewton, so am I wrong here? First, I suspected that but after seeing redux-actions example I though it needs the resolved data. github.com/redux-utilities/redux-promise
– devserkan
Aug 10 at 15:41



redux-actions





"If it receives a promise, it will dispatch the resolved value of the promise. It will not dispatch anything if the promise rejects." If you do an async/await you're just creating a regular (async) action. Which is fine, but makes redux-promise seem not that helpful anymore. (Although I stopped using redux-promise for other reasons anyway, it was a useful abstraction for awhile.)
– Dave Newton
Aug 10 at 15:42






Got it. I missed that part and look through the example. This confused me. I am going to update my answer. Thanks!
– devserkan
Aug 10 at 15:44





@devserkan I think that all makes sense to me now. However, i'm not getting an error in my console stating: Uncaught ReferenceError: regeneratorRuntime is not defined.
– Jared Mudd
Aug 10 at 16:08




axios.get() returns promise.



So you need use async / await.



You can simplify your code and avoid dispatching asynchronous actions and needing to use redux middleware by doing the following:


fetchContent()


mapDispatchToProps


fetchContent()



fetchContent() would look like this:


fetchContent()


export async function fetchContent()
const request = await axios.get(`$API_BASE_URL/spaces/$API_SPACE_ID/environments/master/entries?access_token=$API_KEY`);
return
type: FETCH_CONTENT,
payload: request.data.items
;



connect would look like this:


connect


const mapStateToProps = (state) =>
return contents: state.contents.all;


const mapDispatchToProps = (dispatch) =>
return
loadItems: () => fetchContent().then(action => dispatch(action))



// export default CharacterIndex;
export default connect(mapStateToProps, mapDispatchToProps)(ContentIndex);



your reducer would look like this:


export default function(state = INITIAL_STATE, action)
switch(action.type)
case FETCH_CONTENT:
return ...state, all: action.payload ;

default:
return state;




and componentDidMount() would look like this:


componentDidMount()


componentDidMount()
this.props.loadItems();





@JaredMudd friendly reminder to accept an answer if it provides the needed info
– brian-lives-outdoors
Aug 13 at 15:00






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Firebase Auth - with Email and Password - Check user already registered

Dynamically update html content plain JS

How to determine optimal route across keyboard