Nested Query fetch Data - javascript

categories name
pic of datas
import {useState, useEffect,useMemo} from 'react';
import { gql, useQuery } from '#apollo/client';
import {GET_CLOTHES} from "./GetClothes.js"
import ArrowForwardIosIcon from '#mui/icons-material/ArrowForwardIos';
import ArrowBackIosNewIcon from '#mui/icons-material/ArrowBackIosNew';
function Clothes() {
const { loading, error, data } = useQuery(GET_CLOTHES);
const [products,setProducts] = useState([]);
const [index,setIndex] = useState(0);
useEffect(() => {
if (data) {
setProducts(data.categories.products);
console.dir(data.categories.products);
}
},[data]);
//console.dir(products);
/*
useEffect(() => {
if(!loading && data){
setProducts(data);
}
}, [loading, data])
*/
if (loading) return 'Loading...'
if (error) return `Error! ${error.message}`
return <div>
{
// Products is actually products
products?.map((product,index) => {
if (product.name === 'clothes') { //filter
// Iterate through products.products to obtain the product data.
const {name,brand,description,gallery,category} = product;
return <p>{name}</p>
}})
}
</div>
}
export default Clothes;
`
Hello everyone, I am trying to fetch my datas from graphlq however I can fetch to first data from array but when I try reach one of the nested array I got undefined.
How I can solve this issue.

You don't need to keep data as a state variable
data will be undefined until loading is false. The first pass through your console.log it will be undefined.
A typical pattern might be:
function Clothes() {
const { loading, error, data } = useQuery(GET_CLOTHES);
if (data) {
… render the data
} else if (error) {
…handle the error
return <Error /> // return an error component
} else return <Loading /> // return a spinner or other progress indicator
}
Note that your function must return a jsx component - it cannot return a string.

Ok. First of all, categories is an array, so products isn't defined as a property in the array. Its items do have the products property.
Second of all, products is a derived state. You do not need make a separate it as its own useState
import {useState, useEffect,useMemo} from 'react';
import { gql, useQuery } from '#apollo/client';
import {GET_CLOTHES} from "./GetClothes.js"
import ArrowForwardIosIcon from '#mui/icons-material/ArrowForwardIos';
import ArrowBackIosNewIcon from '#mui/icons-material/ArrowBackIosNew';
function Clothes() {
const { loading, error, data } = useQuery(GET_CLOTHES);
const [index,setIndex] = useState(0);
if (loading) return 'Loading...'
if (error) return `Error! ${error.message}`
return <div>
{
data.categories.flatMap(x => x.products).map(product => {
if (product.name === 'clothes') { //filter
// Iterate through products.products to obtain the product data.
const {name,brand,description,gallery,category} = product;
return <p>{name}</p>
} else {
return null;
}
})
}
</div>
}
export default Clothes;

Related

Can't access prop in another component?

I'm getting myself confused with React here (total newbie). I have a simple component that fetches some data that always returns {"score":100}:
import React, { useEffect, useState } from "react";
import Graph from "./Graph.js";
const UsingFetch = () => {
const [results, setResults] = useState({"score": null}); // initially set score to null
const fetchData = () => {
fetch("https://myapi.com/id=1")
.then((response) => {
return response.json();
})
.then((data) => {
setResults(data); // update results with integer score
});
};
useEffect(() => {
fetchData();
}, []);
console.log(results)
return (
<div>
<Graph results={results.score}></Graph>
</div>
);
};
export default UsingFetch;
My Graph.js looks like the following:
import { React } from 'react'
export default function Graph({results}) {
console.log(results)
return (
<div>
<h1>{results}</h1>
</div>
)
}
Why doesn't the score render on the page? I've confirmed that the data returns correctly, I just can't seem to access it right.
Here's the console output:
Results is an array.
<h1>{results.map((result) => (result.score)}</h1>

I lost props after reloading the page in react

I used axios in useEffect of my wrapper component and I sent the data as props to the other component "singleQuestionnaire", in singleQuestionnaire component, I destructured the data, in the first try, it works fine, but after reloading the page it doesn't work with an error : can not read property "map" of undefined
import React, { useEffect, useState } from "react";
import SingleQuestionnaire from "./SingleQuestionnaire";
import { fetchQuestions } from "../../../api/index";
const Questionnaires = ({ match }) => {
const [questions, setQuestions] = useState([]);
const pid = match.params.id;
const getQuestionnaire = async (pid) => {
try {
const { data } = await fetchQuestions(pid);
console.log(data.data, "action in component");
setQuestions(data.data);
} catch (error) {
console.log(error);
}
};
useEffect(() => {
getQuestionnaire(pid);
}, []);
console.log("all questions", questions);
return (
<div>
<SingleQuestionnaire questions={questions} setQuestions={setQuestions} />
</div>
);
};
export default Questionnaires;
and this is my singleQuestionnaire component:
import React, { useEffect, useState } from "react";
const SingleQuestionnaire = ({ questions, setQuestions }) => {
const [questionnaire, setQuestionnaire] = useState([]);
console.log(questions);
const { data } = questions;
console.log("data", data.farmInformationQuestionnaireData);
return <div>simple component</div>;
};
export default SingleQuestionnaire;
For the first time, in console I can see the data "data.data.farmInformationQuestionnaireData". It's an array but for the second time it's undefind.
because questions in SingleQuestionnaire is an empty array before we fetch
which causes an error here
const { data } = questions;
you can add a loading text because initially questions will be an empty array then it will be your res.data (assuming it's an object)
const SingleQuestionnaire = ({ questions, setQuestions }) => {
const [questionnaire, setQuestionnaire] = useState([]);
console.log(questions);
if(questions.length === 0 ) return <h1> Loading</h1>
const { data } = questions;
console.log("data", data.farmInformationQuestionnaireData);
return <div>simple component</div>;
};
it is happening because of the async API call. When you make an async call, the thread does not wait, it moves on and it starts executing other things.
Now your async call might be complete but your callback will not be executed until the stack is empty, that's just how javaScript works. I recommend you use some kind of loader gif or text
{questions ? <SingleQuestionnaire questions={questions} setQuestions={setQuestions} /> : <p>Loading...</p>}

React show data from array of objects

Today I made a useFetch hook to get all the data from a certain category. As you can see on the image it's possible to see all the data in JSON format. Also you can see that it is in an array of objects. I was wondering how I can show this data in normal format like on the page. Most of the time I'm getting the error of data.name NULL. But as you can see the data is fetched correctly in JSON format on the image. I just don't understand how to show all this data normally. Any suggestions?
enter image description here
enter image description here
import React from "react";
import "../Style/menu.css";
import { useParams, withRouter } from "react-router-dom";
import useFetch from "../ApiService/useFetch";
import { render } from "#testing-library/react";
const Product = () => {
const { id } = useParams();
const { data, error, isPending } = useFetch("http://localhost:8080/products/category/" + id);
return (
<p>{JSON.stringify(data)}</p>
)
}
export default Product;
import { useState, useEffect } from "react";
const useFetch = (url) => {
const [data, setData] = useState(null);
const [isLoading, setIsPending] = useState(true);
const [error, setError ] = useState(null);
useEffect(() => {
fetch(url) //custom url so you can reuse it
.then(res => {
if(!res.ok) {
throw Error('could not fetch data');
}
return res.json();
})
.then(data => {
setData(data);
setIsPending(false)
setError(null)
})
.catch(err => {
setError(null)
setIsPending(false)
})
}, [url]);
return {data, isLoading, error} //use properties with custom hook
}
export default useFetch;
This might be helpful for you
...
const Product = () => {
const { id } = useParams();
const { data, error, isPending } = useFetch("http://localhost:8080/products/category/" + id);
return (
{ data && data.length &&
data.map((row) =>{
<p>row.name</p>
})
}
)
}
...
your useFetch is async, as I can see isPending variable, why don't you use this ?
const { id } = useParams();
const { data, error, isPending } = useFetch("http://localhost:8080/products/category/" + id);
return (
<p>{isPending ? null : JSON.stringify(data)}</p>
)

getting TypeError: movieDetails.map is not a function when try to use useState in useEffect

import { useSelector, useDispatch } from "react-redux";
import { useEffect, useState } from "react";
import request from "../../requests";
import { fetchMovies } from "../../feautures/movies/moviesSlice";
import "./SingleMoviePage.scss";
import Rating from "../../components/UI/Rating/Rating";
import axios from "axios";
const SingleMoviePage = ({ match }) => {
const dispatch = useDispatch();
const [movieDetails, setMovieDetails] = useState({})
/* params */
const movieId = match.params.id;
const page = match.params.page;
const genre = match.params.genre;
/* movies reducer handle */
const movies = useSelector((state) => state.movies.movies);
const moviesStatus = useSelector((state) => state.movies.status);
/* movieDetails reducer handle */
/* base urls */
const baseImgUrl = "https://image.tmdb.org/t/p/original";
const movieDetailUrl = `https://api.themoviedb.org/3/movie/${movieId}?api_key=c057c067b76238e7a64d3ba8de37076e&language=en-US`;
useEffect(() => {
const fetchData = async() => {
let response = await axios.get(movieDetailUrl);
response = response.data;
setMovieDetails(response)
}
fetchData()
},[movieDetailUrl])
console.log("data: ",movieDetails )
let content;
if (moviesStatus === "loading") {
<div>Loading ...</div>;
} else if (moviesStatus === "succeeced") {
let movie = movies.find((movie) => movie.id.toString() === movieId);
content = (
<div
className="single-movie__container"
style={{
backgroundImage: `url(${
movie.backdrop_path
? baseImgUrl + movie.backdrop_path
: baseImgUrl + movie.poster_path
})`,
}}
>
<div className="single-movie__information">
<h1 className="single-movie__title">{movie.title}</h1>
<div className="single-movie__rate">
<Rating
rating={movie.vote_average}
className="single-movie__stars"
/>
<div className="single-movie__average">
{movie.vote_average}(Imdb)
</div>
</div>
<p className="single-movie__overview">{movie.overview}</p>
<p className="single-movie__genres">
<label>Genres</label>
{
movieDetails.genres.map(genre => {
console.log("genre: ",genre)
return(
<div>{genre.name}</div>
)
})
}
</p>
</div>
</div>
);
}
useEffect(() => {
if (genre === "POPULAR") {
dispatch(fetchMovies(request.fetchPopular(page)));
} else if (genre === "NOW PLAYING") {
dispatch(fetchMovies(request.fetchNowPlaying(page)));
} else if (genre === "UP COMING") {
dispatch(fetchMovies(request.fetchUpComing(page)));
}
}, [dispatch, genre, page]);
return <div className="single-movie">{content}</div>;
};
export default SingleMoviePage;
I'm trying to make a movie website with react-redux. The issue is when I try to get movie details using useEffect and try to map that in:
<p className="single-movie__genres">
I get TypeError: Cannot read property 'map' of undefined error and I get empty object (data: {}) using console.log("data: ", movieDetails).
But if I refresh the page everything works well and I get
data:
{
adult: false,
backdrop_path: "/6MKr3KgOLmzOP6MSuZERO41Lpkt.jpg",
...
}
using console.log("data: ", movieDetails). Why can't I get data when the page is first loaded?
It is because your initial state does not contain "genres" array inside the object. And when react tries to handle
movieDetails.genres.map(...)
it fall down because movieDetails.genres is undefined (and undefined does not support map method of course). Either include empty array in you initial state like:
const [movieDetails, setMovieDetails] = useState({genres:[]})
or use "?" operator in your chain like:
movieDetails.genres?.map(...)
.map method is a prototype function for type array. you should declare moviedetails as an array like this when setting the default value using useState hook.
const [movieDetails, setMovieDetails] = useState([])
There is a point that i dont understand.As far as i know when the component is first loaded first useEffect worked and filled my movieDetails with datas after that map func worked.I mean js works top to bottom and
shouldn't movieStatus be filled with data until it comes to the map function?

How to wait for server response before calling Apollo Graph QL Query?

I'm attempting to call a Graph QL Query after receiving data from my useEffect hook. I need the data from the response to use in the Query. Hooks however cannot be called conditionally. If I take away the condition however, loadedAnime will be undefined. How do I get around this restraint?
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import AnimeBanner from "../components/AnimeBanner";
import { useHttpClient } from "../Hooks/http-hook";
import { GetAnimeData } from "../GraphQLFunctions";
import { useQuery } from "#apollo/react-hooks";
import gql from "graphql-tag";
const GET_ANIME_INFO = gql`
query GetAnimeInfo($name: String!) {
Media(search: $name) {
title {
romaji
english
native
userPreferred
}
episodes
id
bannerImage
}
}
`;
const Anime = (props) => {
//Logic for getting anime data from mongoDB (episodes, name, cover image)
const { isLoading, error, sendRequest } = useHttpClient();
const [loadedAnime, setloadedAnime] = useState();
const URLTitle = useParams().URLTitle;
useEffect(() => {
const fetchAnime = async () => {
try {
const responseData = await sendRequest(
"http://localhost:5000/api/anime/" + URLTitle
);
setloadedAnime(responseData.animeData[0]);
} catch (err) {
console.log(err);
}
};
fetchAnime();
}, [sendRequest, URLTitle]);
if (isLoading || error) {
return null;
}
//Logic for getting anime data from anilist (Descriptions, tags, banner, trailer, etc.)
const { apiData, apiLoading, apiError } = useQuery(GET_ANIME_INFO, {
variables: {
name: loadedAnime.anime_name,
},
});
if (apiLoading || apiError) {
return null;
}
return <AnimeBanner src={apiData.Media.bannerImage} />;
};
export default Anime;
Short Answer: You can checkout useLazyQuery instead of useQuery.
Documentation link: https://www.apollographql.com/docs/react/data/queries/#executing-queries-manually
When React mounts and renders a component that calls the useQuery hook, Apollo Client automatically executes the specified query. But what if you want to execute a query in response to a different event, such as a user clicking a button?
The useLazyQuery hook is perfect for executing queries in response to events other than component rendering. This hook acts just like useQuery, with one key exception: when useLazyQuery is called, it does not immediately execute its associated query. Instead, it returns a function in its result tuple that you can call whenever you're ready to execute the query
import React, { useState } from 'react';
import { useLazyQuery } from '#apollo/client';
function DelayedQuery() {
const [dog, setDog] = useState(null);
const [getDog, { loading, data }] = useLazyQuery(GET_DOG_PHOTO);
if (loading) return <p>Loading ...</p>;
if (data && data.dog) {
setDog(data.dog);
}
return (
<div>
{dog && <img src={dog.displayImage} />}
<button onClick={() => getDog({ variables: { breed: 'bulldog' } })}>
Click me!
</button>
</div>
);
}
You can either call the query after the await finishes or you can call your query in another useEffect once you update state after your api call. In general, something like this,
const [state, setState] = useState({})
useEffect(async () => {
const result = await get('/api/blah-blah-blah')
// run your query here now that the await has resolved
}, [someDependency])
or
const [state, setState] = useState({})
useEffect(async () => {
const result = await get('/api/blah-blah-blah')
setState(result)
}, [someDependency])
useEffect(() => {
if(state.id) {
// run the query
}
}, [state.someProp])

Categories