Uzzar Uzzar - 14 days ago 5
React JSX Question

Application state doesn't get updated after api call

I am missing a key step/concept in how asyn calls work in react-redux.

Currently, I am able to make api calls (can also see the data being returned via chrome-dev-tools) BUT the response data isn't
reflected in the application state; meaning the state of the quiz object, which by default is an empty object, doesn't get updated.

My expectation is that when the asyn call resolves, I parse through the response in the quizReducer, and return a new state (not a mutated state) that reflects the response data.

Yet, each time the call is made, the updated state returns an empty quiz object (which is the initial state).

I know I am missing something but I can't figure out what; would really appreciate some pointers/explanation. Thank you

The app had an initialState/Preloaded state that looks like this:

export default {
quizzes: [
{id: 0, name: "quiz_name2", completed: false, selected: false},
{id: 1, name: "quiz_name2", completed: false, selected: false}
],
quiz: { questions: {} }
};


Setup for the reducer in question:



import initialState from './initialState';
import quizConstants from '../constants/quizConstants';
const actionTypes = quizConstants.actions

// when app loads, user has not selected any quiz therefore
// initialState is an empty object
// here action is the payload we get when a quiz is selected (see QUIZ_SELETED action creator)


export default function quiz(state=initialState.quiz, action) {
switch(action.type){
// when a quiz is selected, return new state
case actionTypes.SELECT_QUIZ:
return Object.assign(
{},
state,
{
id: action.quiz.id,
name: action.quiz.name,
completed: action.quiz.completed,
selected: !action.quiz.selected,
fetching: action.quiz.fetching,
fetched: action.quiz.fetched,
questions: action.quiz.questions
}
)
case actionTypes.REQUEST_QUIZ:
return Object.assign(
{},
state,
{
id: action.quiz.id,
name: action.quiz.name,
completed: action.quiz.completed,
selected: !action.quiz.selected,
fetching: !action.quiz.fetching,
fetched: action.quiz.fetched,
questions: action.quiz.questions
}
)
case actionTypes.RECEIVE_QUIZ:
return Object.assign(
{},
state,
{
id: action.quiz.id,
name: action.quiz.name,
completed: action.quiz.completed,
selected: !action.quiz.selected,
fetching: action.quiz.fetching,
fetched: !action.quiz.fetched,
quiz: action.quiz.questions
}
)
default:
return state
}
};


index.js (rootreducer):

import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
import quizzes from './quizzesReducer'
import quiz from './quizReducer';

export default combineReducers({
quizzes,
quiz,
routing: routerReducer
});


QuizActionCreators



import quizConstants from '../constants/quizConstants';
import { quizzesEndPoint } from '../constants/appConstants';
import axios from 'axios';

const actionTypes = quizConstants.actions

// select a quiz
export const selectQuiz = (quiz) => {
return {
type: actionTypes.SELECT_QUIZ,
quiz
}
};

const receiveQuiz = (quiz, data) => {
return {
type: actionTypes.RECEIVE_QUIZ,
quiz,
data
}
};

// call when componentWillMount
export const fetchQuiz = (quiz) => {
console.log("Make an api request here")
const url = quizzesEndPoint.concat(quiz.name)
axios.get(url)
.then(response => response.data)
.then(data => receiveQuiz(quiz, data))
}
export default { selectQuiz, fetchQuiz};

Answer

In your QuizActionCreator, your fetchQuiz is calling receiveQuiz and passing quiz and data as your parameter which the latter has the data from the server. I don't see any part in your reducers where you are setting the action.data to the state.

Try adding handler for RECEIVE_QUIZ inside your quizzesReducer and return the action.data to the state.

//quizzesReducer.js
export default function (state = initialState.quizzes, action){
   ...
   if (action.type === actionTypes.RECEIVE_QUIZ) {
      return action.data
   }
   ...
};