I'm trying to display a piece of data from an API, I can reach it, but its giving me an unknown error I cant find an answer for it.
The error message :
Uncaught TypeError: Cannot read properties of undefined (reading 'front_default')
now, here's the funny part, when i first starting the page, its working, but if i refresh it, its giving me the error message on console and a white screen page.
this is the part of my code that don't seems to work.
<img src={urlData.sprites.front_default}></img>
but, similar data can work, such as this line:
<p> {urlData.id}</p>
I'll post the all code under here, but don't waste your time on reading it all, try focus on the error message and error origin in order of helping me identifying the problem.
dad component:
import './App.css';
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { PokemonContainer } from './components/Pokemon';
function App() {
const [pokemonData, setPokemonData] = useState([]);
const [loaded, setLoaded] = useState(false);
const get_pokemon_data = async () => {
setPokemonData((await axios.get('https://pokeapi.co/api/v2/pokemon?limit=9')).data.results);
};
useEffect(() => {
get_pokemon_data();
}, []);
useEffect(() => {
setLoaded(true);
}, [pokemonData]);
if (loaded) {
return (
<div>
{pokemonData.map((pokemon: any) => (
<PokemonContainer key={pokemon.name} url={pokemon.url} />
))}
</div>
);
} else {
return (
<div>
<p>wait a minute</p>
</div>
)
}
};
export default App;
child component :
import React, { useState, useEffect } from 'react';
import axios from 'axios';
export const PokemonContainer = (props: any) => {
const [urlData, setURLData] = useState<any>([]);
const [loaded, setLoaded] = useState<boolean>(false);
useEffect(() => {
const get_url_data = async () => {
setURLData((await axios.get(props.url)).data);
};
get_url_data();
}, []);
useEffect(() => {
setLoaded(true);
}, [urlData]);
if (loaded) {
return (
<div>
<div key={urlData.name}>
<p> {urlData.name}</p>
<p> {urlData.id}</p>
<img src={urlData.sprites.front_default}></img>
</div>
</div>
)
} else {
return (
<div>
<p>Loading...</p>
</div>
)
}
};
I think the problem is with your second useEffect
useEffect(() => {
setLoaded(true);
}, [urlData]);
When component mounts this will set loaded to true but response is not fetched yet and urlData is still an empty array (which should be an empty object, I believe) therefore you get the error.
One way to fix this would be to check if urlData has been fetched inside the useEffect? if yes, then set loaded to true.
But I think this useEffecte is not needed at all. Instead of if(loaded) you could check for one urlData's properties, like: if(urlData.id)
child component:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
export const PokemonContainer = (props: any) => {
const [urlData, setURLData] = useState<any>([]);
useEffect(() => {
const get_url_data = async () => {
setURLData((await axios.get(props.url)).data);
};
get_url_data();
}, []);
if (urlData.id) {
return (
<div>
<div key={urlData.name}>
<p> {urlData.name}</p>
<p> {urlData.id}</p>
<img src={urlData.sprites.front_default}></img>
</div>
</div>
)
} else {
return (
<div>
<p>Loading...</p>
</div>
)
}
};
Or you can do it like:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
export const PokemonContainer = (props: any) => {
const [urlData, setURLData] = useState<any>([]);
useEffect(() => {
const get_url_data = async () => {
setURLData((await axios.get(props.url)).data);
};
get_url_data();
}, []);
if (!urlData.id) {
return (
<div>
<p>Loading...</p>
</div>
);
}
return (
<div>
<div key={urlData.name}>
<p> {urlData.name}</p>
<p> {urlData.id}</p>
<img src={urlData.sprites.front_default}></img>
</div>
</div>
);
};
Related
I am trying to set an array dynamically and render it using useState hook. But it seems the array is not setting. below is my code:
import React, { useState, useEffect } from "react";
export default ({ item }) => {
const [attachments, setAttachments] = useState([]);
const setAttachmentValues = function(response){
setAttachments(response.data);
}
const fetchMedia = async ()=> {
setAttachments([]);
await apiCall().then((response) => {
setAttachmentValues(response);
});
}
useEffect(() => {
fetchMedia();
}, []);
return (
<>
<div className="w-full">
{(attachments.map((ele) => {
<div>{ele}</div>
)}
</>
)
}
apiCall() will return an array of objects.
setState in this way is working well in some cases. What is the actual issue here?
This way you can render data
import React, { useState, useEffect } from 'react';
export default ({ item }) => {
const [attachments, setAttachments] = useState([]);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then((response) => response.json())
.then((response) => {
setAttachments(response);
console.log(response);
});
}, []);
return (
<>
<div>
{attachments.map(item => <div key={item.username}> {item.username} </div> )}
</div>
</>
);
};
I am trying to display a list of towns or departments from an API, I can display the data with console.log but when i put it in html its not working.
here's my ListTown.js:
import React, { useEffect, useState } from "react";
import api from "./api";
function ListTowns() {
const DEPARTEMENTS = "/get/location/departements";
const [departements, setDepartements] = useState([]);
useEffect(() => {
const fetchData = async () => {
const response = await api.get(DEPARTEMENTS).then((response) => {
setDepartements(response.data.data.departements);
});
};
fetchData();
}, []);
return (
<div>
<ul>
{departements.map((dep) => {
<li key={dep.id}>{dep.name}</li>;
})}
</ul>
</div>
);
}
export default ListTowns;
console log (dep.name) gives this result console.log(dep.name)
You forgot to return the data out of your .map method, so right now it's returning undefined for each element.
{departements.map((dep) => {
return <li key={dep.id}>{dep.name}</li>;
})}
I am getting "Cannot read property "map" of undefined (it is referring to tradeData) from the TradeDataList.js file. I am calling an API that is called through search term that is passed to "unirest" get function. I have setup context API passing some centralized values to various other files. I have it set up as an array in the useState. I am loosing my head with this if anyone can help please. The files I have are the following:
context.js
import React, { useState, useContext, useEffect } from 'react'
import { useCallback } from 'react'
var unirest = require('unirest');
const url = 'https://api.someurl.com/oauth/client_credential/accesstoken' ;
const Browse_URL = 'https://api.someurl.com/tradesregister/v1/browse' ;
const MY_API_KEY = 'EpVhCPGX4lzMwzdXVKG7yYFubGtwmlYU4343434';
const Authorization = 'Basic RXBWaENQR1g0bHpNd3pkWFZLRzd5WUZ1Ykd0d21sWVU6M2NvcEVuQTVEZlJXZ3BSYw==' ;
const AppContext = React.createContext()
const AppProvider = ({ children }) => {
const [token, setToken] = useState('') ;
const [loading, setLoading] = useState(true) ;
const [tradeData, setTradeData] = useState([]) ;
const [searchTerm, setSearchTerm] = useState('')
const getToken = () => {
unirest.get(url)
.header({'Accept': 'application/json', 'authorization': Authorization})
.query({"grant_type": "client_credentials"})
.end(function (response) {
const token = response.body["access_token"] ;
//console.log(token) ;
setToken(token)
})
}
useEffect (() => {
// call the getToken function
getToken() ;
}, [])
//Get the new token to access trade data
const newAuth = 'Bearer ' + token
return (
<AppContext.Provider
value={{ token,
MY_API_KEY,
newAuth,
Browse_URL,
loading,
setTradeData,
tradeData,
setLoading,
searchTerm,
setSearchTerm
}}
>
{children}
</AppContext.Provider>
)
}
export const useGlobalContext = () => {
return useContext(AppContext)
}
export { AppContext, AppProvider }
SearchForm.js (call starts from here )
import React, { useEffect, useState } from 'react'
import { useGlobalContext } from '../context'
import TradeDataList from './TradeDataList'
//import Loading from './Loading'
var unirest = require('unirest');
const SearchForm = () => {
const {searchTerm, setSearchTerm,loading, setLoading, tradeData, setTradeData, MY_API_KEY, newAuth, Browse_URL } = useGlobalContext();
//const [tradeData, setTradeData] = useState([]) ;
const getTrade = () => {
setLoading(true)
unirest.get(Browse_URL)
.header({'Accept': 'application/json', 'authorization': newAuth, "apikey": MY_API_KEY })
.query({"searchText": searchTerm })
.end(function (response) {
const tradeData = response.body ;
//console.log(tradeData) ;
setTradeData(tradeData)
setLoading(false);
})
}
useEffect (() => {
// call the getTrade function
getTrade() ;
}, [])
const handleSubmit = (e) => {
e.preventDefault() ;
getTrade() ;
}
console.log('here is the data' ,tradeData)
return (
<section className="section-search">
<form className="search-form" onSubmit={handleSubmit}>
<div className="form-control">
<label htmlFor='searchTerm'> search trade licence info</label>
<input
type='text'
id='searchTerm'
//ref={searchValue} //reference
value={searchTerm}
//onChange={searchTrade}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</div>
<button type="submit">Submit</button>
</form>
<div className="cocktails-center">
<TradeDataList tradeData={tradeData} />
</div>
</section>
)
}
export default SearchForm
TradeList.js
import React from 'react'
import TradeData from './TradeData'
//import Loading from './Loading'
//import { useGlobalContext } from '../context'
/**
* This page displays TradeData and loading
*/
const TradeDataList = ({ tradeData }) => {
//display loading while cocktails are being loaded
return (
<section className="section">
<h2 className="section-title">
Trade Data
</h2>
{/* <h2>cocktail list</h2> */}
<div className="cocktails-center">
{tradeData.map((trade) => {
// this is to be handled by Cocktail component
return <TradeData key={trade.licenceID} {...trade} />
})}
</div>
</section>
)
}
export default TradeDataList
TradeData.js
import React from 'react'
import { Link } from 'react-router-dom'
//image,name,id,info,glass
const TradeData = ({
licenceID,
licensee,
licenceName,
licenceNumber,
licenceType,
status,
suburb,
postcode,
businessNames,
categories,
classes
}) => {
return (
<article className="cocktail">
<div className="img-container">
<h1>This is header </h1>
</div>
<div className="cocktail-footer">
<h3>{licenceID}</h3>
<p>{licensee}</p>
<h4>{licenceName}</h4>
<p>{licenceType}</p>
{/* <Link to={`/cocktail/${id}`} className="btn btn-primary
btn-details">details</Link> */}
</div>
</article>
)
}
export default TradeData
Inside your TradeList.js you simply need to add a check condition:
<div className="cocktails-center">
{tradeData?tradeData.map((trade) => {
// this is to be handled by Cocktail component
return <TradeData key={trade.licenceID} {...trade} />
}):null}
</div>
Another alternative solution
{ tradeData && tradeData.map((trade) => {
// this is to be handled by Cocktail component
return <TradeData key={trade.licenceID} {...trade} />
})}
Note: The .map function is only available on array.
If data isn't in the format you are expecting it to be (it is {} but you are expecting []).
I thought had a better grasp of hooks but I've clearly got something wrong here. Not all of the character objects will have what I'm trying to get but it wont work with those that do.
I cna't even build in a check for character.comics.available. Same errors appear. I'm presuming I'm getting them before the state is set? But {character.name} always works.
CharacterDetail.js
import React from "react";
import { useParams } from "react-router-dom";
import useCharacter from "../hooks/useCharacter";
const CharacterDetail = () => {
// from the Route path="/character/:id"
const { id } = useParams();
// custom hook. useCharacter.js
const [character] = useCharacter(id);
// this only works sometimes but errors if i refresh the page
// console.log(character.comics.available);
return (
<div>
<h2 className="ui header">Character Details</h2>
<p>Works every time: {character.name}</p>
<div className="ui segment"></div>
<pre></pre>
</div>
);
};
export default CharacterDetail;
Custom hook useCharacter.js
import { useState, useEffect } from "react";
import marvel from "../apis/marvel";
const useCharacter = (id) => {
const [character, setCharacter] = useState({});
useEffect(() => {
loadItem();
return () => {};
}, [id]);
const loadItem = async (term) => {
const response = await marvel.get(`/characters/${id}`);
console.log(response.data.data.results[0]);
setCharacter(response.data.data.results[0]);
};
return [character];
};
export default useCharacter;
error when console is uncommented
Uncaught TypeError: Cannot read property 'available' of undefined
at CharacterDetail (CharacterDetail.js:11)
...
Here is the character object.
thanks to #Nikita for the pointers. Settled on this...
CharacterDetail.js
import React from "react";
import { useParams } from "react-router-dom";
import useCharacter from "../hooks/useCharacter";
const CharacterDetail = () => {
const { id } = useParams();
// custom hook. useCharacter.js
const { isLoading, character } = useCharacter(id);
const isArray = character instanceof Array;
if (!isLoading && isArray === false) {
console.log("isLoading", isArray);
const thumb =
character.thumbnail.path +
"/portrait_uncanny." +
character.thumbnail.extension;
return (
<div>
<h2 className="ui header">{character.name}</h2>
<img src={thumb} />
<div className="ui segment">{character.comics.available}</div>
<div className="ui segment">{character.series.available}</div>
<div className="ui segment">{character.stories.available}</div>
</div>
);
}
return <div>Loading...</div>;
};
export default CharacterDetail;
useCharacter.js
import { useState, useEffect } from "react";
import marvel from "../apis/marvel";
function useCharacter(id) {
const [character, setCharacter] = useState([]);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
setIsLoading(true);
const fetchData = async () => {
setIsLoading(true);
await marvel
.get(`/characters/${id}`)
.then((response) => {
/* DO STUFF WHEN THE CALLS SUCCEEDS */
setIsLoading(false);
setCharacter(response.data.data.results[0]);
})
.catch((e) => {
/* HANDLE THE ERROR (e) */
});
};
fetchData();
}, [id]);
return {
isLoading,
character,
};
}
export default useCharacter;
I have trouble with react context.
Especially with function getProductCategory which I use in the second component.
My React context provider looks like :
import React, { createContext, useState, useEffect } from "react";
import data from "./data";
export const ProductContext = createContext();
const ProductContextProvider = ({ children }) => {
const [productsCategory, setProductCategory] = useState();
const [products, setProducts] = useState();
useEffect(() => {
setProducts(data);
});
function getProductCategory(category) {
const productFromCategory = data.filter(
(product) => product.type === category
);
setProductCategory(productFromCategory); //this line is causing the problem
console.log(productFromCategory);
return productsCategory;
}
const getProduct = (product) => {
const currentProduct = products
? products.filter((item) => item.slug === product)[0]
: undefined;
return currentProduct;
};
return (
<ProductContext.Provider
value={{ getProduct, getProductCategory }}
>
{children}
</ProductContext.Provider>
);
};
export default ProductContextProvider;
I want to get access to my context from another component Mats which is a page.
import React, { useContext, useEffect } from "react";
import { ProductContext } from "../../context";
/accesoriesComponent/CategoryTitle/CategoryTitle";
import Filters from "../../components/accesoriesComponent/Filters/Filters";
import ProductWrapper from "../../components/accesoriesComponent/ProductWrapper/ProductWrapper";
export default function Mats(props) {
const { getProductCategory } = useContext(ProductContext);
const mats = getProductCategory("mats");
return (
<div>
<div style={{ display: "flex" }}>
<Filters />
{mats && <ProductWrapper products={mats} />}
</div>
</div>
);
}
When I want to visit page Mats, I get error "Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside component WillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.".
I marked the line of code in the first code, that courses the problem .
I don't know how to handle it.
Any suggestions ?
Based on your code:-
instead of return-ing productsCategory from your getProductCategory function. Send your productsCategory as ProductContext.Provider values props
Btw, your useEffect should have [] or empty array as dependency
ProductContextProvider.js:-
import React, { createContext, useState, useEffect } from "react";
import data from "./data";
export const ProductContext = createContext();
const ProductContextProvider = ({ children }) => {
const [productsCategory, setProductCategory] = useState();
const [products, setProducts] = useState();
useEffect(() => {
setProducts(data);
}, []); // making sure it will only run once when rendered
function getProductCategory(category) {
const productFromCategory = data.filter(
(product) => product.type === category
);
setProductCategory(productFromCategory); // you already set the data in here
console.log(productFromCategory);
// return productsCategory; // no need to return this
}
const getProduct = (product) => {
const currentProduct = products
? products.filter((item) => item.slug === product)[0]
: undefined;
return currentProduct;
};
return (
<ProductContext.Provider
value={{ productsCategory, getProduct, getProductCategory }}
>
{children}
</ProductContext.Provider>
);
};
export default ProductContextProvider;
So in Mats.js, just get display your updated ``
export default function Mats(props) {
const { productsCategory, getProductCategory } = useContext(ProductContext);
// Shouldn't do this. Cause it will keep rerender
// const mats = getProductCategory("mats");
// default state of category
const [category, setCategory] = useState('mats')
// use useffect to send the updated products by category
useEffect(() => {
(() => {
if(category) {
getProductCategory(category);
}
})()
}, [category]) // will initiated every time category change
return (
<div>
<div style={{ display: "flex" }}>
<Filters />
{productsCategory && <ProductWrapper products={productsCategory} />}
</div>
</div>
);
}