I am writing a quiz application using react with redux as state management library.I have written action and reducer to fetch quiz question from an API call and set my state. I am only showing the relevant part of the codebase as it is too large.
actions/index.js
import { START_QUIZ } from "./types";
import { NEW_ROUND } from "./types";
import { ANSWER_QUESTION } from "./types";
import { NEW_QUIZ } from "./types";
import { FETCH_REQUEST } from "./types";
import { FETCH_SUCCESS } from "./types";
import { FETCH_ERROR } from "./types";
import { SHOW_UI } from "./types";
const baseurl = process.env.REACT_APP_BASE_URI;
function fetchDataRequest() {
return {
type: FETCH_REQUEST,
};
}
function fetchDataSuccess(payload) {
return {
type: FETCH_SUCCESS,
payload,
};
}
export function fetchDataWithRedux() {
return (dispatch) => {
dispatch(fetchDataRequest());
return fetchData().then((response) => {
console.log("Now the respose object is" + JSON.stringify(response));
if (response.status === 200) {
dispatch(fetchDataSuccess(response.data));
} else {
dispatch(fetchDataError());
}
});
};
}
async function fetchData() {
const URL = baseurl + "/617da24c8d757b423e0ae9a8";
const response = await axios.get(URL);
return response;
}
Now this is the initial state in /assets/defaults.js
const defaults = {
questionsAndAnswers: [],
userAnswers: [],
};
export default defaults;
Now my quizreducer is reducers/quizreducer.js Please refer only to FETCH_SUCCESS case
import defaults from "../../assets/defaults";
import {
START_QUIZ,
NEW_ROUND,
ANSWER_QUESTION,
NEW_QUIZ,
FETCH_REQUEST,
FETCH_SUCCESS,
} from "../actions/types";
function getRandomNumber(min, max) {
return Math.floor(Math.random() * (max + 1 - min) + min);
}
function quizReducer(state = defaults, action) {
switch (action.type) {
case START_QUIZ:
let newQuiz = Object.assign({}, state);
for (let i = 0; i < 4; i++) {
let randomNumber = getRandomNumber(
0,
newQuiz.unusedQuestions.length - 1
);
let question = newQuiz.unusedQuestions.splice(randomNumber, 1);
newQuiz.currentRoundQuestions.push(question);
}
if (action.payload === "countries") {
newQuiz.questionsAndAnswersReversed = true;
} else {
newQuiz.questionsAndAnswersReversed = false;
}
newQuiz.currentRoundAnswer = getRandomNumber(0, 3);
newQuiz.currentRound++;
return newQuiz;
case ANSWER_QUESTION:
let answeredQuestion = Object.assign({}, state);
if (action.payload) answeredQuestion.userScore++;
return answeredQuestion;
case NEW_ROUND:
let newRound = Object.assign({}, state);
newRound.currentRoundQuestions = [];
newRound.currentRoundAnswer = -1;
for (let i = 0; i < 4; i++) {
let randomNumber = getRandomNumber(
0,
newRound.unusedQuestions.length - 1
);
let question = newRound.unusedQuestions.splice(randomNumber, 1);
newRound.currentRoundQuestions.push(question);
}
newRound.currentRoundAnswer = getRandomNumber(0, 3);
newRound.currentRound++;
if (action.payload) newRound.userScore++;
return newRound;
case NEW_QUIZ:
let newQuizData = Object.assign({}, state);
newQuizData.currentRoundQuestions = [];
newQuizData.currentRoundAnswer = -1;
newQuizData.currentRound = 0;
newQuizData.userScore = 0;
return newQuizData;
case FETCH_REQUEST:
return state;
case FETCH_SUCCESS:
let newFetch = Object.assign({}, state);
newFetch.questionsAndAnswers = action.payload.QA;
newFetch.userAnswers = [];
return newFetch;
default:
return state;
}
}
export default quizReducer;
This is my app.js
import "./App.css";
import Quiz from "./Quiz";
import React, { Component, useEffect } from "react";
import { connect } from "react-redux";
import { fetchDataWithRedux } from "./store/actions/index";
import { bindActionCreators } from "redux";
require("dotenv").config();
function mapStateToProps(state) {
return {
Quiz: state,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(
{
fetchDataWithRedux: fetchDataWithRedux,
},
dispatch
);
}
function App(props) {
console.log("Props are " + JSON.stringify(props));
useEffect(() => {
props.fetchDataWithRedux();
}, []);
return (
<>
<Quiz />
</>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
Now comes the main problem
This is the response api is giving
{
"_id": "617da24c8d757b423e0ae9a8",
"published_at": "2021-10-30T21:35:55.999Z",
"createdAt": "2021-10-30T19:51:40.361Z",
"updatedAt": "2021-10-30T22:03:55.595Z",
"__v": 0,
"Description": "t is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English.",
"Title": "Test Quiz",
"QA": [
{
"Q": "Ich komme heute später aus dem",
"O": {
"A": "Biro",
"B": "Büro",
"C": "Bürro "
},
"A": "B"
},
{
"Q": "was is richtigen Artikel, Auto",
"O": {
"A": "Der",
"B": "Die",
"C": "Das "
},
"A": "C"
}
],
"id": "617da24c8d757b423e0ae9a8"
}
Now according to react the error is Error: Objects are not valid as a React child (found: object with keys {Q, O, A}). If you meant to render a collection of children, use an array instead.
Now there are two strange things I guess the main line giving error is newFetch.questionsAndAnswers = action.payload.QA; But another strange thing is that when I opened up my redux devtools I found the correct data in the state variable Redux devtools
Now another weird thing is that when I did
typeof action.payload.QA
I got the result as object and not array But one can clearly see that response.data.QA is an array of objects and not an object
I have tried several variations like sending JSON.stringify(response.data)
but to no use. I am really badly stuck and unable to figure out a way out of this.
Even the call stack of actions is also correct in my redux devtools as shown
Action call stack
Please help !!!