A problem displaying the content of RestCountries API, where the error of "TypeError: cyclic object value", but I don't have a problem with JSON.stringify?

Context:

  • I am running MX-Linux 21.3, using VS-code, using “vite build” with dependencies {
    @reduxjs/toolkit”: “^1.9.5”,
    “axios”: “^1.4.0”,
    “react”: “^18.2.0”,
    “react-dom”: “^18.2.0”,
    “react-fontawesome”: “^1.7.1”,
    “react-icons”: “^4.10.1”,
    “react-redux”: “^8.1.2”,
    “redux”: “^4.2.1”}, and finally I use this API “https://restcountries.com/v3.1/all”.
    Description:
    First of all, my github repo in this link:
    github repo link
    Second, I suppose to get all the countries from this API and display them in my Home page, but I get error message:

    This is my code in the redux toolkit slice:
    ((countriesSlice.js))
import { createSlice } from "@reduxjs/toolkit";
import { showAllCountries, searchByCode, searchByRegion } from "./countriesAction";

const initialState = {
  loading: false,
  countriesData: [],
  countrySearched: [],
  error: false,
  success: false,
  message: "",
  region: "",
  searchTerm: "",
}

export const countriesSlice = createSlice({
  name: "countries",
  initialState: initialState,
  reducers: {
    reset: (state) => {
      state.loading = false;
      state.success = false;
      state.error = false;
      state.message = false;
      state.countrySearched = [];
      state.region = "";
      state.searchTerm = "";
    }
  },
  setRegion: (state, action) => {
    state.region = action.payload;
  },
  setSearchTerm: (state, action) => {
    state.searchTerm = action.payload;
  },
  extraReducers: (builder) => {
    builder.addCase(showAllCountries.pending, (state) => {
      state.loading = true;
    }).addCase(showAllCountries.fulfilled, (state, action) => {
      state.loading = false;
      state.countriesData = action.payload;
      state.success = true;
    }).addCase(showAllCountries.rejected, (state, action) => {
      state.loading = false;
      state.error = true;
      state.message = action.payload;
      state.countriesData = [];
    }).addCase(searchByCode.pending, (state) => {
      state.loading = true;
    }).addCase(searchByCode.fulfilled, (state, action) => {
      state.loading = false;
      state.countrySearched = action.payload;
      state.success = true;
    }).addCase(searchByCode.rejected, (state, action) => {
      state.loading = false;
      state.error = true;
      state.message = action.payload;
      state.countrySearched = [];
    }).addCase(searchByRegion.pending, (state) => {
      state.loading = true;
    }).addCase(searchByRegion.fulfilled, (state, action) => {
      state.loading = false;
      state.countriesData = action.payload;
      state.success = true;
    }).addCase(searchByRegion.rejected, (state, action) => {
      state.loading = false;
      state.error = true;
      state.message = action.payload;
      state.countriesData = [];
    })
  }
})

export const { reset, setRegion, setSearchTerm } = countriesSlice.actions;
export default countriesSlice.reducer;

((countriesAction.js))

import axios from "axios";
import { createAsyncThunk } from "@reduxjs/toolkit";

// show all countries async function
export const showAllCountries = createAsyncThunk("countries/showAll", async(_, thunkAPI) => {
  try {
    const response = await axios.get(`https://restcountries.com/v3.1/all`);
    // console.log(response.data)
    return response.data;

  } catch(err) {
    const message = (err.response && err.response.data) || err.message;

    // this function (rejectWithValue) will send the error message as a payload to our page to be displayed.
    return thunkAPI.rejectWithValue(message);
  }

});

// search country by using cioc code :

export const searchByCode = createAsyncThunk("countries/searchByCode", async(code, thunkAPI) => {
  try {
    const response = await axios.get(`https://restcountries.com/v3.1/alpha/${code}`);
    return response.data;
  } catch (err) {
    const message = (err.response && err.response.data) || err.message;
    console.log(message);
  } finally {
    console.log(thunkAPI);
  }
})

// search country by using region :

export const searchByRegion = createAsyncThunk("countries/searchByRegion", async(region, thunkAPI) => {
  try {
    const response = await axios.get(`https://restcountries.com/v3.1/region/${region}`);
    return response.data;
  } catch (err) {
    const message = (err.response && err.response.data) || err.message;
    console.log(message);
  } finally {
    console.log(thunkAPI);
  }
})

((countryDetail.jsx))

import "./country-detail.css";
import { useEffect } from "react";
import { Link, useParams } from "react-router-dom";
import { searchByCode } from "../../features/countries/countriesAction";
import { reset } from "../../features/countries/countriesSlice";

// import from Redux :
import { useSelector, useDispatch } from "react-redux";

const CountryDetail = () => {

  const { err, countrySearched } = useSelector((state) => state.country );
  const dispatch = useDispatch();
  const {code} = useParams();

  useEffect(() => {
    if(code){
      dispatch(searchByCode(code.toLowerCase()));
    }
    if(err){
      console.log(err);
    }
    return () => {
      dispatch(reset)
    }
  }, [dispatch, code, err])

  return (
    <section className="country-detail-container">
      <Link className="back-button" to="/">
        <i className="fa-solid fa-arrow-left"></i> Back
      </Link>

      <div className="country-detail-content">
      {countrySearched.length > 0 ? (
        <>
          <img src={countrySearched[0].flags.png} alt={countrySearched[0].flags.alt} className="country-detail-image" />
          <div className="country-detail-right">
            <h1>{countrySearched[0].name.common}</h1>
            <div className="details">
              <div className="detail-left">
                <p>
                  Offcial Name: <span>{countrySearched[0].name.official}</span>
                </p>
                <p>
                  Population: <span>{countrySearched[0].population}</span>
                </p>
                <p>
                  Region: <span>{countrySearched[0].region}</span>
                </p>

                <p>
                  Sub Region: <span>{countrySearched[0].subregion}</span>
                </p>
                <p>
                  Capital: <span>{countrySearched[0].capital}</span>
                </p>
              </div>

              <div className="detail-right">
                <p>
                  Top Level Domain: <span>{countrySearched[0].tld[0]}</span>
                </p>
                <p>
                  Currencies:
                  <span>
                    {Object.values(countrySearched[0].currencies).map((item) => {
                      return item.name;
                    }).join(",")}
                  </span>
                </p>

                <p>
                  Languages:
                  <span>
                    {Object.values(countrySearched[0].languages).map((item) => {
                      return item;
                    }).join(",")}
                  </span>
                </p>
              </div>
            </div>

            <div className="border">
              <p>Border Countries:</p>
              {countrySearched[0].borders ? 
                countrySearched[0].borders.map((item, index) => {
                  return (<Link className="border-name" key={index} to={`${item}`}>
                    <p>{item}</p>
                  </Link>)}
                ) : (<span> No Borders </span>)}
            </div>
          </div>
        </>
      ) : <div> No Details Found! </div> }

      </div>
    </section>
  );
};

export default CountryDetail;

I have a doubt in the dispatching methods like:

dispatch(searchByCode(code.toLowerCase()));

But I feel the error message is misleading.
I hope I made it clear and provide the problem in a descriptive way.
I would appreciate any help and thanks in advance for your cooperation.

  • what do you have in line 13?
  • what debug have you tried?
  • whats expected instead?

talking about these would might also help figuring out whats causing this problem, happy coding :slight_smile:

Hello bappyasif,
this is Home.jsx file:

import Country from "../../components/country/Country"
import Filter from "../../components/input/filter/Filter";
import Search from "../../components/input/search/Search";
import "./home.css";

const Home = () => {
  return (
    <section className="home-page-container">
      <div className="input-container">
        <Search />
        <Filter />
      </div>
      <Country />
    </section>
  );
};

export default Home;

and I have component on line 13.
I have tried to debug using Redux extensions and Network tab and I found error 304, then I thought that there is a CORS problem, but when I ran Postman and try to “get” API, nothing went wrong and everything was OK.
I have tried to console log response.data and also everything is fine.
I don’t know what’s expected instead! Do you mean what should I expect from that error output? As I said before, I think the problem maybe comes from “dispatch function” but I’m not sure.
Do you want to know anything else? I am ready to answer.

I didn’t really look at the code but the circular reference error usually means you are assigning it back to itself (object reference).

Hi Lasjorg, how are you ?
Yes, I have read this article and I cannot figure out what’s going on. I also asked chatGPT for help, and here it is :slight_smile:

  1. Inspect the Stack Trace: Look at the stack trace provided in the error message. It indicates where the problem occurred in your code. Start by inspecting the parseData function at line 8 and its usage on line 10. Follow the chain of calls to identify where the cyclic object reference is originating.
  2. Check Data Structures: Review the data structures you are using, especially objects and arrays, to see if there are any circular references. Circular references commonly happen when an object contains a reference to itself or when two objects reference each other.
  3. Avoid Circular References: If you find any circular references, you’ll need to break them. For example, if you have an object that references itself, you should modify the object so that it doesn’t point to itself directly.
  4. Use JSON.stringify() Safely: If you are using JSON.stringify() to convert objects to JSON strings, make sure to handle circular references using the replacer function. The replacer function allows you to omit specific properties or transform them before serialization. This prevents the JSON.stringify() method from throwing an error.

Do you have any idea what’s going on?

Not without reading and testing the code.

I briefly ran the code but got an error, setSearchTerm is undefined inside Seach.jsx. setRegion and setSearchTerm are outside the reducer object in your createSlice.

Also, fixing that I do not see the error you are talking about but nothing happens when I search or filter, well I see the loading state for a split second, but nothing else shows up, and as said I don’t get the error.

Edit: Did you forget to push your code? Does your repo have your latest code?

I have fixed (setSearchTerm, setRegion) and I pushed the project again to github repo.
Can you take a look, please?
Thank you.

I still don’t see that error and when I look in the Redux dev tools I see the state update.

If I manually go to /someCountryCode it also shows me the country information (after I have searched for it first).


Also, is the search not supposed to trigger a route update?

Hi lasjorg,
I did not found an error as you said, but I cannot display the whole countries as it supposed to be although when I console logged (countriesData), it gives me all 250 countries objects. Here is a screenshot:


I updated git repo to the latest and I have tried again but in this case I hard typed “TUR” in the URL and it showed me this:

I guess it’s a little mistake, I cant figure it out,

???

You are not returning anything inside your map in the Country component.

1 Like

:crazy_face: :crazy_face: :crazy_face: :crazy_face: :crazy_face:
I feel like a dumb A$$ …
Thank you so much … I really appreciate your efforts … problem solved!

It is a common mistake. The good news is, the more times you make it, the less likely you are to make it again without catching it.

Happy to have helped.

1 Like