0

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 !!!

AK10
  • 13
  • 3
  • `typeof [] === 'object'` this is a given, to actually check if something is an array you have to use `Array.isArray(action.payload.QA)`. But if you're calling `map()` on `action.payload.QA` that probably isn't the problem, I would guess you are returning an object from the map, but that is the one part of you code you didn't include... – pilchard Dec 23 '21 at 20:23
  • I am simply assigning newFetch.questionsAndAnswers = action.payload.QA; – AK10 Dec 23 '21 at 20:29
  • React expects an array of Components, but you are giving it an array of objects which *'are not valid as React children'*. You need to map over the array and parse each object into a consumable Component or valid HTML – pilchard Dec 23 '21 at 20:31
  • Does this answer your question? [How to map an array of objects in React](https://stackoverflow.com/questions/41027663/how-to-map-an-array-of-objects-in-react) – pilchard Dec 23 '21 at 20:33
  • This is state management. I think it has got nothing to do with components. I want to set my state variable to response.data.QA which is an array of objects, that's it. – AK10 Dec 23 '21 at 20:33
  • No it does'nt in my case I am not even handling how to display the data in component. I just want to set my redux state variable. – AK10 Dec 23 '21 at 20:36
  • Well you wouldn't be getting that error if you weren't trying to render an unparsed object, so while it might be related to state management, the specific error you are getting is render related. Specifically it is complaining about trying to render an object with the keys `{Q, O, A}` – pilchard Dec 23 '21 at 20:36
  • The question of component comes when I have to handle UI part. Here it is just setting the state. I know Array.prototype.map and how to use it to display data – AK10 Dec 23 '21 at 20:37

0 Answers0