Passing props to URL - javascript

I am using deckofcardsapi.com and I cannot pass my deck_id in props to fetch url.
Props are passing ok because when I use them in line 40 they display as normal string. And when I paste this string to variable deckId in line 23 it is giving me list of cards.
But when instead of copying string to deckId i use props.deckId there is error "TypeError: cards.cards is undefined"
On the other hand props.quantity works well.
Here is my code
import React, { useState, useEffect } from 'react';
function NewDeck(props) {
const [deck, setDeck] = useState({ deck_id: [] });
async function draw() {
const result = await fetch(`https://deckofcardsapi.com/api/deck/new/shuffle/?deck_count=1`);
const json = await result.json();
setDeck(json);
};
useEffect(() => {
draw();
}, []);
return(
<Draw deckId={deck.deck_id} quantity={props.quantity} />
);
}
function Draw(props) {
const [cards, setCards] = useState({ cards: [] });
var deckId = props.deckId; //when i put here for egzample "l31hmefvilqe" it is working
async function draw() {
const result = await fetch(`https://deckofcardsapi.com/api/deck/${deckId}/draw/?count=${props.quantity}`);
const json = await result.json();
setCards(json);
};
useEffect(() => {
draw();
}, []);
return(
<ul>
{cards.cards.map(item => (
<li>{item.code}</li>
))}
<li>deckId: {props.deckId}</li>
<li>quantity: {props.quantity}</li>
</ul>
);
}
export default NewDeck;
How to pass props.deckId to my fetch's url
I was searching for answer but with no result. This is probably my stupid mistake but i can't find it.
Thank you in adwance

The problem is that at the first render of NewDeck the value of deck_id isn't set and it pass an empty array. You can fix it rendering the Draw component conditionally to the deck_id having a value . https://codesandbox.io/s/brave-margulis-olgxt In my example i set the deck_id to null and render draw only when deck_id exists.
PD: props.quantity is undefined and maybe you meant deck.remaining? Also check the draw() dependency for useEffect in the component, maybe you need to useCallback() (I0m not so sure of this because I'm still learning hooks)

Related

In React, how can I make a single api call, set the result to a state variable, and display it?

I am programming a react application where I need to make a single async api call, save the result to a state variable and display the result. I am using an axios instance where the response of the api call is a nested object. For example:
{
kanji:
{character: "", stroke:""},
quote: "string"
}
So far I have the following code where I am able to console.log the homeData object successfully. However I get the error: 'TypeError: homeData is undefined'.
const Home = () =>{
const [homeData, setHomeData] = useState({})
const getHomeData = async()=>{
instance.get("/data")
.then(res =>{
setHomeData(res.data)
})
.catch(error =>{
console.log(error)
})
}
useEffect(()=>{
getHomeData()
}, [])
console.log(homeData)
return(
<>
<p>{homeData}<p>
</>
)
}
This seems like a promise issue but I am not sure how to fix it. Thank you for your help in advance.
This is not a promise issue, it has to due with the order in which the lifecycle methods run. useEffect runs after the component renders. This means when the component first renders, the homeData is an empty object. homeData will be present in the second render. The following code first checks if homeData exists, then if it exists, it displays the kanji character. Note also, you cant just display a raw object into the dom so you will have to access the homeData by property for it to display
const Home = () =>{
const [homeData, setHomeData] = useState({})
const getHomeData = async()=>{
instance.get("/data")
.then(res =>{
setHomeData(res.data)
})
.catch(error =>{
console.log(error)
})
}
useEffect(()=>{
getHomeData()
}, [])
console.log(homeData)
return(
<>
<p>{homeData && homeData.kanji.character}<p>
</>
)
}

Can't access state inside function in React Function Component

I have the following component in React:
import React, { useState, useEffect } from 'react';
import api from '../../services/api';
const Store = () => {
const [products, setProducts] = useState([]);
let pagination = null;
const getProducts = async (page = 1) => {
const { data } = await api.get('products', { params: { page } });
setProducts([...products, ...data.products]);
pagination = data.pagination;
if (pagination.current < pagination.max) {
document.addEventListener('scroll', loadMore);
}
};
const loadMore = () => {
const { scrollTop, clientHeight, scrollHeight } = document.documentElement;
if (scrollTop + clientHeight >= scrollHeight - 300) {
getProducts(pagination.current + 1);
document.removeEventListener('scroll', loadMore);
}
};
useEffect(() => {
getProducts();
}, []);
useEffect(() => {
console.log(products);
}, [products]);
return (
<div>
{products.map(product => (
<p>{product.name}</p>
))}
</div>
);
};
export default Store;
My console.log inside the useEffect hook do print the products array correctly, and the component is also rendering the products titles correctly.
But when I try to access the products variable inside the getProducts function it doesn't get the updated products value, it gets the value I have set in the useState hook.
For example, if I start the state with one product, calling products within the getProducts function will always bring this one product, and not the ones loaded from the API fetch, that were correctly logged in the console.
So, when I try to add more products to the end of the array it actually just add the products to an empty array.
Any idea why this is happening? Being able to access the products state inside the useState hook but not inside the getProducts function?
This is happening because the getProducts is using the value products had when getProducts was being declared, not the one products has when the getProducts function is called. This is always the case when you want to access the most recent state directly in non-inline functions. Mostly, you will need to pass the updated value through the function arguments.
But this is not an option in your case. The only option in your case is to use the previous value as the argument from a callback passed to setState (in your case, setProducts).
Hope this helps someone else in future :).

I keep getting map is not a function

import React, { useState, useEffect } from "react";
import { checkRef } from "./firebase";
function Dashboard() {
const [count, setCount] = useState([]);
let hi = [];
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
checkRef.on("value", snapshot => {
let items = snapshot.val();
let newState = [];
for (let tracker in items) {
newState.push({
reason: items[tracker].reason,
teacher: items[tracker].teacher
});
}
setCount({ items: newState });
});
// Update the document title using the browser API
// checkRef.on('value',(snapshot) => {
// console.log(snapshot.val())
// })
}, []);
return (
<div>
{count.items.map(item => {
return <h1>{item.reason}</h1>;
})}
</div>
);
}
export default Dashboard;
I am trying to return each item as an h1 after getting the item but i keep getting the error ×
TypeError: Cannot read property 'map' of undefined. I apoligize i AM new to web dev and trying to learn. I have spent way to much time with no results. thanks
Problem lies with your initalization of count.
For the very first render your count state variable doesn't contain any property named items. hence it fails.
your state variable is getting items prepery only after useEffect which excutes after first render.
based on your useeffct code, you should initialize count state variable with an object like follwing,
const [count, setCount] = useState({items:[]});
It sounds like useEffect is not calling the call back function (the one you defined).
You could try initializing the count.items to be an array
Or make sure that useEffect calls the callback function
Another way to fix your issue is simply do this:
{
count && count.items && count.items.map( item => {
// do something with the item
})
}

React hooks not set state at first time

i have a problem with hooks.
i'm using react-hooks, i have a button which at onClick getting data from api and setState with it.
Problem is:
when i click to button first time i get response from api but can't set it to state. When click to button second time i can setState. Why it happened ?
here is my component look like:
function App() {
const [a, setA] = useState(null);
const fetchData = () => {
let data = {
id: 1
}
axios.post(baseUrl, data)
.then(res => {
if (res.data) {
setA(res.data)
}
console.log(a)
})
}
return (
<div className="App">
<div>
<button onClick={fetchData}></button>
</div>
</div>
);
}
export default App;
i tried to using fetchData function like that:
function fetchData() {
let data = {
id: 1
}
axios.post(baseUrl, data)
.then(res => {
if (res.data) {
setA(res.data)
}
console.log(a)
})
}
but it's not helped too
a is a const. It's impossible to change it, so there's no way your console.log statement at the end of fetchData could ever log out something different than the value that a had when fetchData was created.
So that's not what setA does. The point of setA isn't to change the value of the const, but to cause the component to rerender. On that new render, a new set of variables will be created, and the new a will get the new value. Move your console.log out to the body of your component, and you'll see it rerender with the new value.
In short: Your code appears to be already working, you just have the wrong logging.
If your scope is to fetch data, use this:
const [data, setData] = useState("");
useEffect(async () => {
const result = await axios(
'here will be your api',
);
setData(result.data);
});
Now your response will be stored in data variable.
I would not use an effect for it, effects are useful if the props or state changes and can thereby substitute lifecycle methods like componentDidMount, componentDidUpdate, componentWillUnmount, etc.. But in your case these props haven't changed yet (you want to change your state though). Btw, be aware that #Asking's approach will fetch data on EVERY rerender (props or state change). If you want to use useEffect, be sure to add the second parameter to tell React when to update.
Normally, your code should work, I haven't tested but looks legit. Have you used developer tools for react to check if the state/hook has changed? Because if you say it did not work because of the console.log printing: Have in mind that setA() is an async function. The state was most likely not yet changed when you try to print it. If you haven't react developer tools (which I highly recommend) you can check the real state by adding this code in the function:
useEffect(() => console.log(a), [a]);
I have a few real improvements to your code though:
function App() {
const [a, setA] = useState(null);
const fetchData = useCallback(async () => {
let data = {
id: 1
}
const res = await axios.post(baseUrl, data);
setA(res.data);
}, []);
return (
<div className="App">
<div>
<button onClick={fetchData}></button>
</div>
</div>
);
}
export default App;
By adding useCallback you ensure that the function is memorized and not declared again and again on every rerender.
#Nicholas Tower has already answered your question. But if you are looking for code
function App() {
const [a, setA] = useState(null);
const fetchData = () => {
let data = {
id: 1
}
axios.post(baseUrl, data)
.then(res => {
if (res.data) {
setA(res.data)
}
})
}
console.log(a)
return (
<div className="App">
<div>
<button onClick={fetchData}></button>
</div>
</div>
);
}
export default App;
just place log before return (. This should do

Objects are not valid as React child... but it IS an array?

This function is returning the error:
"Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead."
and I can't figure out why. It shouldn't be returning an object, it should be mapping over an array, right? At the top of the code I'm using a hook for setState like so:
const [expSymbolData, setExpSymbolData] = useState([])
I have a similar code that's working properly. Also, when an error like this happens, is there a way to see what object it's returning instead of React just saying "nope, use an array!"? It would help me troubleshoot.
const GetSymbol = async () => {
const rezsymbol = await fetch("https://api.scryfall.com/sets/83491685-880d-41dd-a4af-47d2b3b17c10")
const jsonsymbol = await rezsymbol.json()
setExpSymbolData(jsonsymbol)
{
expSymbolData.map((expStuff) => {
return(
<span>{expStuff.icon_svg_url}</span>
)}
)
}
}
useState setter is not a synchronous action. values will be updated on next render so either you can use useEffect to render based on updated values or simply use the data returned by api
const GetSymbol = async () => {
const rezsymbol = await fetch("https://api.scryfall.com/sets/83491685-880d-41dd-a4af-47d2b3b17c10")
const jsonsymbol = await rezsymbol.json()
setExpSymbolData(jsonsymbol)
return jsonsymbol.map((expStuff) => <span>{expStuff.icon_svg_url}</span>);
}
// Alternate
const GetSymbol = async () => {
const rezsymbol = await fetch("https://api.scryfall.com/sets/83491685-880d-41dd-a4af-47d2b3b17c10")
const jsonsymbol = await rezsymbol.json()
setExpSymbolData(jsonsymbol)
}
// add a useEffect hook
useEffect(() => {
expSymbolData.map((expStuff) => <span>{expStuff.icon_svg_url}</span>);
}, [expSymbolData]);
In 2nd scenario, handle rending based on new logic

Categories

Resources