Can anyone explain.Why the 'res" is getting logged two times? - reactjs

I am trying to get the images data from flickr. I am getting the response but not able to display the image.I have tried using useEffect to fetch the api and useState variable to store images array. Still it was getting called two times.
const DisplayImages = () => {
let photos = [];
fetch(
` https://www.flickr.com/services/rest/?method=flickr.galleries.getPhotos&api_key=***********************&gallery_id=************&format=json&nojsoncallback=1`
)
.then((response) => response.json())
.then((res) => {
const images = res;
console.log(res);
photos = images.photos.photo;
});
return (
<>
<div className="imagescontainer">
{photos.map((photo) => {
console.log(photo);
let url = `https://farm${photo.farm}.staticflickr.com/${photo.server}/${photo.id}_${photo.secret}`;
<>
{/* <img src={require(url)} alt="img" /> */}
</>
})}
</div>
</>
);
};
I expected the images to be rendered.

It's getting called twice because React is probably configured to be in strict mode in development, which causes hooks to be called twice.
Second, you're using a locally-scoped variable photos to hold state, when you need to use useState instance, otherwise React won't know when the state changes and re-render the component.
Third, it looks like you're setting photos, which is initialized with an empty array, to a single instance of a photo.

Related

React hooks not showing correct data when component is rendered

I have created a hook in a component as below.
useEffect(() => {
axios
.get("http://127.0.0.1:5000/v1/matches")
.then((response) => {
getStatusCode(response.data.code);
console.log("responseCode",responseCode);
getMatchdata(response.data.result);
setInfo(<MatchData responseCode={responseCode} matchdata={matchdata} />);
})
.catch((error) => console.log(error));
},[]);
This is a state function used in the above effect
const [info, setInfo] = useState();
I expected the above useEffect should return me some data in the below block
<div> {info} </div>
but it is showing wrong data, whereas I have created another function to trigger on Refresh button as
function refresh() {
setInfo(<MatchData responseCode={responseCode} matchdata={matchdata} />);
}
this function is returning me correct data. I want to create a functionality that will dynamically update the div element with change in state of {info}, by default when the page is loaded first, it should fetch data from the endpoint used here only. I'm new to React. Where I'm going wrong and how do I achieve it?
I don't want to say this is wrong, but this seems like an atypical approach from what I've seen in the wild. Specifically I am talking about storing a JS/JSX or TS/TSX element in a state object. I have more commonly seen a value stored in that type of variable and that value changing when necessary via the set dispatch function. Then the state object is passed to the component who needs it to do something. In react, when the value of that state object changes, it will cause the component who uses it to re-render. If I were coding this, this is what my code would look like.
const [info, setInfo] = useState();
const getData = () => {
axios
.get("http://127.0.0.1:5000/v1/matches")
.then((response) => {
setInfo(response.json())
})
.catch((error) => console.log(error));
}
const divComponent = ({info}) => (
<div>
<p>{info.data.code}</p>
<p>{info.data.result}</p>
</div>
)
const refreshButton = () => (
<button onClick(()=>getData())>Refresh</button>
)
Unless you only specifically want something to happen once at component mount, you would not use useEffect() like you did in your code. If the decision to refresh were coming from an external object with state instead of the refresh button, you could add that object whose state changes to the dependency array of the useEffect function. This would cause the refresh to run any time that object's state value changes. In the code above, getData() (which might need to be async) will only run when called. Then you have a component called divComponent which is expecting info to have value. When rendering this component you would want a null check like I coded below. Finally the refreshButton component will call getData() when it is clicked.
Then in your code that renders this, I would have something like this:
<>
{info ? <divComponent info={info} /> : <p>There is no info</p>}
<refreshButton />
</>
The code above will check if the state object info has value, and if it does it will render the divComponent with your data values. If it does not, instead it will show the p tag explaining that there is no data. Either way it will render the refreshButton, which would run the getData() function again when clicked.
** EDIT **
Based on your comment, here is another approach so you can have a value on page load and update when necessary:
import {useState, useEffect} from "react";
const [info, setInfo] = useState();
const getData = () => {
axios
.get("http://127.0.0.1:5000/v1/matches")
.then((response) => {
setInfo(response.json())
})
.catch((error) => console.log(error));
}
useEffect(()=> {
getData();
}, [])
const divComponent = ({info}) => (
<div>
<p>{info.data.code}</p>
<p>{info.data.result}</p>
</div>
)
const refreshButton = () => (
<button onClick(()=>getData())>Refresh</button>
)
export const Page = () => (
<>
{info ? <divComponent info={info} /> : <p>There is no info</p>}
<refreshButton />
</>
);
your method is quite complex. I believe you need to add your MatchData Component Inside the div in this way.Also Don't Need To Call State Method setInfo() in useEffect hook.Only responseCode and matchdata Needed that is already adjusted by you in useEffect Hook.

Why is the data displayed only through the map in react?

I only need to display the data of one object that I get here
const {posts, loading} = useSelector((state) => ({...state.posts}))
useEffect(() => {
dispatch(getPost(id))
}, []);
"posts" is array
when I want to display it simply this way
<h4>cat: {posts[0].category}</h4>
<h4>aut: {posts[0].username}</h4>
it only works only for the first time and after one page refresh the array (posts) turns out to be empty. But when I do it like this
<>
{
posts.map(post=>(
<CardDetailsComponent post={post} key={post._id}/>
))
}
</>
(CardDetails component looks exactly like first example but in different component)
everything is fine. Why?

Can't Update state after fetch request

I'm trying to make an app where I fetch data from a Graphql API, and update state after fetching, but after the successful request I'm getting the data but I can't update state with that data. when I console log the response data it's showing the array with required data but when I update state and console log the state it's showing empty array.
I need to update the state to use it in the component, when i'm doing this it's throwing error that currList is undefined.
here are pictures of code and console.
export default function App() {
const [search, setSeach] = useState("");
const [currList, setCurrList] = useState([]);
const fetchShips = async () => {
const response = await request(gql`
{
ships {
name
home_port
image
}
}
`);
console.log("request response", response.data);
setCurrList(response.data);
console.log("currlist:", currList);
};
useEffect(() => {
fetchShips();
}, [currList]);
const Searchitems = (event) => {
setSeach(event.target.value);
setCurrList(
currList.filter((item) => {
return item.name.includes(search) || item.home_port.includes(search);
})
);
};
return (
<div className="App">
<div className="container">
<header></header>
<div className="body">
<input
type="text"
id="search"
value={search}
onChange={Searchitems}
className="input"
placeholder="Search Ships"
/>
<p>Total Count: {currList.length}</p>
<div className="ships">
{currList.map((item, index) => {
return (
<Ship
key={index}
name={item.name}
port={item.port}
img={item.image}
/>
);
})}
</div>
</div>
</div>
</div>
);
}
the State is Updated just not when the code is running that's why it logs that the state is an empty array, try to console.log it once again and you will see that there is something in the List.
That's normal, everything is happening asynchronously. React will trigger a re-render once your currList is updated and you will get its new value. If you want to listen to this change, you have to use useEffect with currList as a dependency.
useEffect(() => {
console.log("List is updated", currList);
}, [currList]);
The function fetchShips has closure over the currList state variable that blongs to specific render call, so when you log it after calling setCurrList it shows you the previous state not the updated state, you can log it in the useEffect to see the changes.
I had the same problem once. I couldn't find efficient solution but my solution saved me anyway.
I used useRef instead of useState.
Try this:
const currList = useRef([]);
useEffect=(()=>{
const fetchShips = async () => {
const response = await request(gql`
{
ships {
name
home_port
image
}
}
`);
console.log("request response", response.data);
// setCurrList(response.data);
if(response)
currlist.current = response.data
console.log("currlist:", currList);
};
fetchShips()
// Also as far as I know ,you should use useEffect like this
},[])
//... codes
return(
//... codes
currList.current.map(...
)
//... codes
Before using useRef, try to define your fetchShips function inside useEffect so maybe you don't need my solution.
Why is not efficient my solution for your case?
When you want to update your currList data, useRef does not trigger re-render. Even if your data updated, you cannot see it on your screen.
So setCurrList(currList.current) can save you but as I said earlier it may not efficient way.

How to add fresh data to an existing data in React-query?

I have a code which calls an API with a certain number of data and receives them. I wish for the 2nd call to add the newly retrieved data to be added to the already existing data using React-query library.
My code so far is :
const fetchData = async ({queryKey : [key , pages]}) => {
return (await fetch(`http://jservice.io/api/random?count=${pages}`)).json()
}
function App() {
const [state, setState] = React.useState(10);
const {isLoading, error, data , } = useInfiniteQuery(['general' , state], fetchData , {keepPreviousData : true} )
setTimeout(() => {
setState(20)
}, 4444);
console.log(data)
return (
<div className="App">
h
</div>
);
}
I wish to hit the 4444 time line marker by adding the newly fetched data to the existing data solely by using React-query hooks.
How can I achieve this?
You need to use the fetchNextPage from the query. Call this to load the next page. Either onClick or after a timeout.
<button onClick={fetchNextPage}>Load more</button>
Also do not set setTimeout in the render function, since it will be called and executed on every render, which will remove your progress.
Dont track the current page yourself but let react-query handle it.
Just set the getNextPageParam function to return how you need to get the next page and extract that as pageParam from the query context in fetchData
To render the data, you need to map over the pages returned from the query inside data and for each page, query over the items returned:
{isSuccess &&
data.pages.map((page, index) => (
<div key={index}>
{page.results.map((poke) => (
<div key={poke.name}>{poke.name}</div>
))}
</div>
))}
Here is a sandbox with pokemon.

Cannot read property 'questions' of undefined

In this application, I am trying to create a quiz application.
import React, { useState, useEffect, Component } from 'react';
const axios = require('axios').default;
const PlayQuiz = () => {
// declaring all the state here
const [questionsArray, setQuestionsArray] = useState([]);
// Using effects here
useEffect(() => {
axios({
method: 'get',
url: 'https://opentdb.com/api.php?amount=10',
}).then(res => {console.log(Object.values(res.data)[1]); setQuestionsArray(Object.values(res.data)[1])})
.catch(err => console.error(err))
}, []);
useEffect(() => {console.log(questionsArray)}, [questionsArray]);
// Returning html markup here
return (<>
<div className = 'questions-container'>
{/* {questionsArray.map(questionObject => <h1>{questionObject.question}</h1>)} */}
<h1>{questionsArray[0].question}</h1>
</div>
</>)
}
export default PlayQuiz;
(btw all the console logs that this code contains is just for me to visualise of what exactly is going on)
In the following code, I use axiosto fetch data from the API and then resolve the data in my questionsArray. Then I want to print a heading tag into my dom which contains the first element inside my array, i.e. the object and get that object's question property which contains the actual question. But when I do this: <h1>{questionsArray[0].questions}</h1>, it throws an error, saying cannot read property questions of undefined.
Just in case, if anyone of you want to see the object that I get from the API:
And from this object, I get the results object value from the data key in the object, and set it to questionsArray.
If anyone of you wants to see what gets stored inside my questionsArray:
How can I fix this error?
You can try optional chaining ?.
<h1>{questionsArray[0]?.question}</h1>
Check this out:
https://codesandbox.io/s/strange-lovelace-06u3x?file=/src/App.js
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
When React is rendering your data, axios is not completely fetced your data and your questionsArray is either undefined or empty. Just check if axios is finished fetching data.
return (<>
<div className = 'questions-container'>
{/* {questionsArray && questionsArray.map(questionObject => <h1>{questionObject.question}</h1>)} */}
<h1>{questionsArray[0].question}</h1>
</div>
</>)
This will ensure your data is defined or not empty

Resources