I am fetching data
I have a brand property and I am trying to display it, and I am trying to implement a click setCurrentSelectedBrand logic to diplay the data of that specific brand[object]. Currently I am getting the TypeError: Cannot read property 'brand' of undefined error in the Main Component, I dont know why
Data
[{
"a_class":[
{
"brand":"A-class",
"id":"1",
"year":"2015"
"price":"12665"
...
"engine_spec":{
...
}
...
}],
"b_class":[
{
"brand":"B-class",
"id":"2",
"year":"2016"
"price":"12665"
...
"engine_spec":{
...
}
...
}],
}
]
Main Component
const Cars = () => {
const { cars, handleSelectBrand} = useContext(CarsContext)
return (
<div>
{Object.keys(cars).map((key:any, index)=>{
let brands:any = cars[key];
return (
<div key={key} onClick={() => handleSelectBrand(key)} className='brand__list' >
{brands[0].brand}
</div>
);})}
<CarsDetails />
</div>
)
}
export default Cars
Context
declare module 'axios' {
export interface AxiosResponse<T = any> extends Promise<T> {}
}
export const CarsProvider:React.FC <IProps> = ({ children } ) => {
const [isLoading, setIsLoading] = useState(false);
const [cars, setCars] =useState< any | ICars[] >([])
const [brands, setBrands] = useState([])
const [currentSelectedBrand, setCurrentSelectedBrand] = useState('')
const handleSelectBrand = React.useCallback((brand) => {
return setCurrentSelectedBrand(cars[brand]);
},[cars])
useEffect(()=>{
const fetchData = async () => {
setIsLoading(true);
const response = await api.get('/cars', {
});
setIsLoading(false)
setCars([...response.data]);
setCurrentSelectedBrand(response[Object.keys(response)[0]]);
console.log(response.data)
};
fetchData()
},[brands, cars])
Related
Can I perform client-side data fetching inside a component that's being rendered on a server-side rendered page? I have a page located at pages/solution/[solutionId]/index.js, which is server-side rendered, and it contains three components that should be performing client-side fetching. However, I am not getting any data, and it is returning null.
index.js
const Solution = ({ solution }) => {
const [isOpen, setIsOpen] = useState(false)
const router = useRouter()
const { id } = router.query
const { user } = useAuthContext()
return (
<>
<div className="px-5 row-start-2 row-end-3 col-start-2 col-end-3 mb-4">
// doing client-side fetching
<ShowWebsite
url={solution?.liveWebsiteUrl}
github={solution?.githubUrl}
title={solution?.title}
isPlayground={solution?.isPlayground}
/>
<div className="grid grid-col-1 md:grid-cols-[1fr_160px] items-start gap-x-5 mt-10">
<SolutionComments /> // doing client side fetching
<EmojiSection /> // doing client side fetching
</div>
</div>
</>
)
}
export default Solution
export async function getServerSideProps({ query }) {
const { solutionId } = query
console.log(solutionId)
const solution = await getDocument("solutions", solutionId)
return {
props: {
solution,
},
}
}
SolutionsComment:
import { useState } from "react"
import { useRouter } from "next/router"
import { useCollection } from "../../hooks/useCollection"
import Comment from "./Comment"
import CommentForm from "./CommentForm"
const SolutionComments = () => {
const router = useRouter()
const { id } = router.query
const { documents } = useCollection(`solutions/${id}/comments`)
return (
<div className="mt-10 md:mt-0">
<CommentForm docID={id} />
<div className="mt-10">
{documents &&
documents.map((comment) => (
<Comment
key={comment.id}
comment={comment}
replies={comment.replies}
/>
))}
</div>
</div>
)
}
EmojiSection:
import React from "react"
import { useRouter } from "next/router"
import { useDocument } from "../../hooks/useDocument"
import Emoji from "./Emoji"
const EmojiSection = () => {
const router = useRouter()
const { id: docID } = router.query
const { document: reactions } = useDocument(`solutions/${docID}/reactions`, "emojis")
console.log(reactions)
return (
// JSX CODE
)
}
useCollection:
import { collection, onSnapshot} from "firebase/firestore"
export const useCollection = (c) => {
const [documents, setDocuments] = useState([])
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => {
let ref = collection(db, c)
const unsubscribe = onSnapshot(ref, (snapshot) => {
const results = []
snapshot.docs.forEach(
(doc) => {
results.push({ ...doc.data(), id: doc.id })
},
(error) => {
console.log(error)
setError("could not fetch the data")
}
)
// update state
setDocuments(results)
setIsLoading(false)
setError(null)
})
return () => unsubscribe()
}, [])
return { documents, error, isLoading }
}
Cannot fix this error.
I receive data (object) from thingspeak.com via fetch method. Then I want to render three values from that object: name, temperature and time. I can see temperature and name but not time.
Here is my error in console:
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading '0').
Any ideas?
import React, { useState, useEffect } from 'react'
const App = () => {
const [channelInfo, setChannelInfo] = useState({});
const [isLoading, setIsLoading] = useState(true);
const [dateWithOffset, setDateWithOffset] = useState(null);
const getThermoData = async () => {
setIsLoading(true);
const response = await fetch(`https://api.thingspeak.com/channels/618304/fields/1.json?results=1`);
const data = await response.json();
setChannelInfo(data);
setIsLoading(false);
timeConverter();
}
const timeConverter = () => {
const channelDate = channelInfo.feeds[0].created_at;
const date = new Date(channelDate);
const timestampWithOffset = date.getTime();
setDateWithOffset(new Date(timestampWithOffset));
}
useEffect(() => {
getThermoData();
}, [])
return (
<div className='card'>
{
isLoading ? <p className='loading'>Loading...</p> : <>
<h5 className='channel-name no-select'>{channelInfo.channel.name}</h5>
<p className='temperature-value no-select'>{channelInfo.feeds[0].field1}</p>
<h5 className='channel-time no-select'>{dateWithOffset}</h5>
</>
}
</div>
)
}
export default App
enter image description here
Calling timeConverter just after setting the new state with setChannelInfo(data); will throw you an exception due to 1) Initial channelInfo value is {} so there is no .feeds[0].created_at and 2) calling setChannelInfo will not update the variable immediately. It will be updated on next render only.
You can switch to useMemo, add undefined check, change initial state of channelInfo to undefined.
import { useState, useEffect, useMemo } from "react";
const App = () => {
const [channelInfo, setChannelInfo] = useState();
const [isLoading, setIsLoading] = useState(true);
const dateWithOffset = useMemo(() => {
if (!channelInfo) return "";
const channelDate = channelInfo.feeds[0].created_at;
const date = new Date(channelDate);
const timestampWithOffset = date.getTime();
return new Date(timestampWithOffset).toString();
}, [channelInfo]);
const getThermoData = async () => {
setIsLoading(true);
const response = await fetch(
`https://api.thingspeak.com/channels/618304/fields/1.json?results=1`
);
const data = await response.json();
setChannelInfo(data);
setIsLoading(false);
};
useEffect(() => {
getThermoData();
}, []);
return (
<div className="card">
{isLoading ? (
<p className="loading">Loading...</p>
) : (
<>
<h5 className="channel-name no-select">{channelInfo.channel.name}</h5>
<p className="temperature-value no-select">
{channelInfo.feeds[0].field1}
</p>
<h5 className="channel-time no-select">{dateWithOffset}</h5>
</>
)}
</div>
);
};
export default App;
I'm trying to display the response from the API into my react component but it's not working. If I try to use it in the console, I can see the data and its value but not in the react component, it's empty when I try to show the value in a div.
Here is the code where I'm trying to display it in my react component:
const CharacterListing = () => {
const characters = useSelector(getAllCharacters);
console.log("Hello", characters);
const renderCharacters = Object.entries(characters).map(([key, value]) => {
console.log(value.name);
<div>{value.name}</div>
})
return (
<div>
{renderCharacters}
</div>
);
};
export default CharacterListing;
This is the code for my Character Slice Component
const initialState = {
characters: {},
};
const characterSlice = createSlice({
name: 'characters',
initialState,
reducers: {
addCharacters: (state, { payload }) => {
state.characters = payload;
},
},
});
export const { addCharacters } = characterSlice.actions;
export const getAllCharacters = (state) => state.characters.characters;
export default characterSlice.reducer;
This is the code for my Home Component:
const Home = () => {
const dispatch = useDispatch();
useEffect(() => {
const fetchCharacters = async () => {
const response = await baseURL.get(`/characters`)
.catch(error => {
console.log("Error", error);
});
dispatch(addCharacters(response.data));
console.log("Success", response);
};
fetchCharacters();
}, [])
return (
<div>
Home
<CharacterListing />
</div>
);
};
export default Home;
Thank you
You forgot to return item into your map func
Try this :
const renderCharacters = Object.entries(characters).map(([key, value]) => {
console.log(value.name);
return <div key={key}>{value.name}</div>
})
I have a component that fetches the data properly but I want to encapsulate it in a helper. I've tried many things but I'm stuck.
This is the component that works:
export const Carousel = () => {
const [ lotteries, setLotteries ] = useState({});
const [ isLoading, setisLoading ] = useState(true);
useEffect(() => {
async function fetchAPI() {
const url = 'https://protected-sea-30988.herokuapp.com/https://www.lottoland.com/api/drawings;'
let response = await fetch(url)
response = await response.json()
setLotteries(response)
setisLoading(false)
}
fetchAPI()
}, [])
return (
<>
{
isLoading ? (
<span>loading</span>
) : (
<Slider >
{
Object.keys(lotteries).map((lottery, idx) => {
return (
<Slide
key={ idx }
title={ lottery }
prize={ lotteries[lottery].next.jackpot }
day={ lotteries[lottery].next.date.day }
/>
)
})
}
</Slider>
)}
</>
);}
And this is the last thing I've tried so far. This is the component without the fetch
export const Carousel = () => {
const [ lotteries, setLotteries ] = useState({});
const [ isLoading, setIsLoading ] = useState(true);
useEffect(() => {
getLotteries()
setLotteries(response)
setIsLoading(false)
}, [])
And this is where I tried to encapsulate the fetching.
export const getLotteries = async() => {
const url = 'https://protected-sea-30988.herokuapp.com/https://www.lottoland.com/api/drawings;'
let response = await fetch(url)
response = await response.json()
return response;
}
I'm a bit new to React, so any help would be much appreciated. Many thanks.
To get the fetched data from getLotteries helper you have to return a promise
export const getLotteries = async() => {
const url = 'https://protected-sea-
30988.herokuapp.com/https://www.lottoland.com/api/drawings;'
let response = await fetch(url)
return response.json()
}
and call it as async/await
useEffect(async() => {
let response= await getLotteries()
setLotteries(response)
setIsLoading(false)
}, [])
If you want to separate the logic for requesting a URL into another helper function, you can create a custom hook.
// customHook.js
import { useEffect, useState } from 'react';
export function useLotteries() {
const [lotteries, setLotteries] = useState(null);
useEffect(() => {
fetch('https://protected-sea-30988.herokuapp.com/https://www.lottoland.com/api/drawings;')
.then(response => response.json())
.then(json => setLotteries(json));
}, []);
return lotteries;
}
// Carousel.js
import { useLotteries } from "./customHook.js";
export const Carousel = () => {
const lotteries = useLotteries();
if (lotteries) {
return; /* Your JSX here! (`lotteries` is now contains all the request responses) */
} else {
return <Loader />; // Or just null if you don't want to show a loading indicator when your data hasn't been received yet.
}
};
So the thing I am trying to do is to destructure coinsData, so I can use the id globally and keep the coinsData so I can iterate it somewhere else. Right now I am having a problem with typescript on export CoinProvider Type '({ children }: { children?: ReactNode; }) => void' is not assignable to type 'FC<{}>'Help would be appreciated
import React,{FC, useState, useEffect} from 'react'
export interface Coin {
id:string;
name: string;
current_price: number;
symbol:string;
price_change_percentage_24h:number
image:string;
market_cap:number
market_cap_rank:number
}
export const CoinContext = React.createContext<Coin[] | undefined>(undefined)
export const CoinProvider:FC= ({children}) => {
const [loading, setLoading] =useState(false)
const [page, setPage] = useState(1);
const [totalPages, setTotalPages] = useState(10);
const [coinsData, setCoinsData] = useState<Coin[]>([])
const handlePrevPage = () => {
setPage((prevPage) => prevPage - 1);
};
const handleNextPage = () => {
setPage((nextPage) => nextPage + 1);
};
useEffect(()=>{
const fetchData= async()=>{
setLoading(true);
const response= await fetch(`https://api.coingecko.com/api/v3/coins/markets?
vs_currency=usd&order=market_cap_desc&page=${page}&per_page=10&sparkline=false`)
const result = await response.json()
setCoinsData(result);
setTotalPages(totalPages);
setLoading(false)
}
fetchData()
},[page, totalPages])
const Coins = coinsData.map((item) => {
const {
id,
} = item
return(
<CoinContext.Provider value={{Coins, totalPages, id, loading, handlePrevPage, handleNextPage,
currentPage:{ page }}}>
{children}
</CoinContext.Provider>
)
}
You forgot about }) in line 47.
And logic there should look like this:
const Coins = coinsData.map((item) => item.id)
Your component is missing a return statement.
Make sure to return an HTML that can be rendered.
return <>
{coinsData.map((item) => {
const { id } = item
return (
<CoinContext.Provider value={[{ id: 'id', current_price: 1, image: 'image', market_cap: 0, market_cap_rank: 0, name: 'name', price_change_percentage_24h: 0, symbol: 'symbol' }]}>
{children}
</CoinContext.Provider>
)
})
}
</>
And, don't forget to close the map function with the parenthesis )
In case, you only want to pass coinsData
return <CoinContext.Provider value={...coinsData}>
{children}
</CoinContext.Provider>
you can renounce the map and simplify your code with destructuring your coinsData directly.