I am fetching the data from the API (fake REST API built with json-server) in my react app. I want the app to be updated automatically without refreshing manually as any changes occur in the API. How do I do this? the code is below:
import './App.css'
import React, {useState, useEffect} from 'react'
import axios from 'axios'
export default function App() {
const [post, setPost]=useState([])
const url="http://localhost:3000/users"
useEffect(()=>{
axios
.get(url)
.then(res=>{
setPost(res.data)
})
.catch(err=>{
console.log(err)
})
},[])
const showUsers=
post.map(item=>{
return(
<div className="card" key={item.id}>
<h2>Name: {item.name}</h2>
<h3>Username: {item.username}</h3>
<p>Email:{item.email}</p>
</div>
)
})}
return (
<div className="container">
{showUsers}
</div>
)
}
NOTE I attempted to put post state variable, in the dependency array of useEffect hook, and it worked as I wanted but it started to send infinite get requests to the server. Something like that
useEffect(()=>{
axios
.get(url)
.then(res=>{
setPost(res.data)
})
.catch(err=>{
console.log(err)
})
},[post])
First, let's talk about why putting post in the useEffect dependency causes infinite requests. As stated:
You can tell React to skip applying an effect if certain values haven’t changed between re-renders.
but the problem is that every time you call the effect, you actually change post by calling setPost. this causes a re-render. while react is doing the re-render it checks whether it should run the effect again or not. since the variable post in the useEffect has changed, react decides to run the effect again. this causes the infinite loop.
Back to your main question. If I understood correctly you want to sync your application's state with the server's state regarding posts. You can achieve that by polling or websockets. in polling the client send a request to the server every few seconds to check if there has been any chaneges in the state. websockets make it possible to have a two-way connection between the server and client. the two-way connection makes it possible for the server to send information to the client at any time.
below is a very simple polling solution to your problem. I strongly encourage you to read more about the concept.
// ommited code
const [post, setPost]=useState([])
const url="http://localhost:3000/users"
const WAIT_TIME = 5000;
useEffect(() => {
const id = setInterval(() => {
axios
.get(url)
.then(res=>{
setPost(res.data);
})
.catch(err=>{
console.log(err);
})
}, WAIT_TIME);
return () => clearInterval(id);
}, [post]);
// omitted code
By default useEffect run on each update.
If you give ,[post])
It tell React to skip applying an effect if certain values (post) haven’t changed between re-renders.
If you want fetch only at mount use an ,[])
The react doc explain this very well:
Optimizing Performance by Skipping Effects
Related
I have a couple of query calls that were working previously, but now are not firing off, while the many others I have throughout the app continue to work perfect. I've done some updating and adjustments elsewhere in the app, but none of which should have affected these calls. On the Redux dev tool, the calls never even show, as if they never happen. The only error I get is from the subsequent data being undefined.
UPDATE/EDIT
I've dialed in the issue, but am still confused. If I comment out the call and only display the status (isSuccess, isLoading, isError) the call goes out, completes, and returns success and I can verify the data in the devtools. However if I try to use the data, react is crashing before the data is returned.
Here's one of the calls:
import React from 'react';
import { useGetUsersQuery } from '../redux/apiSlice';
import { SupervisorTab } from './userviews/SupervisorTab';
export default function Teams() {
const { data } = useGetUsersQuery()
const teams = data.filter(e => e.role === "Supervisor")
return(
<div>
<h1>Teams</h1>
{teams && teams.map(t => <SupervisorTab supervisor={t} key={t._id} /> )}
</div>
)
}
and the corresponding endpoint on the apiSlice:
getUsers: builder.query({
query: () => '/userapi/users',
providesTags: ['User']
}),
I attempted to provide a useEffect hook to only interact with the data once the call is successful, but the same issue is occurring even within the hook.
const [teams, setTeams] = useState()
const { data, isSuccess, isLoading, isError, error } = useGetUsersQuery()
let content
if (isLoading) {
content = <h1>Loading...</h1>
} else if (isError) {
content = <h1>Error: {error}</h1>
} else if (isSuccess) {
content = <h1>Success</h1>
}
useEffect(()=>{
console.log(data)
//below are 2 scenarios that illustrate the issue, they're run at separate times...
setTeams(data)
//this will provide the correct data, set it to state, and the 2nd log below shows the same.
const teamInfo = data.filter(e => e.role === "Supervisor") //
setTeams(teamInfo)
//this call fails saying data is undefined and the initial console log states undefined
}, [isSuccess])
console.log(teams)
I've not had an issue with this before, typically I put in the query hook, it gets called and completed before the final render, without any UI crash for undefined values. Still, using useEffect, it should only interact with the data once it is available (isSuccess), yet it is crashing during the attempt to interact within useEffect.
I'm ok with React, but have not seen this behavior before. If anyone has a clue as to why or how to resolve, please let me know.
I'm requesting API data through axios using RapidAPI's Apis.
I followed all the documentations provided in RapidAPI with a relatively simple code. However, when I log the values, it keeps repeatedly requesting data over and over and this in turn hikes up my requests from RapidAPI and uses a lot of resources and money. I can't figure out how to only retrieve the values ONCE. Here are my codes.
My React .tsx file
const [sportsData, setSportsData] = useState()
const fetchSportsData = () => {
axios
.request(testApi)
.then((response) => {
setSportsData(response.data)
})
.catch((error) => {
console.log(error)
})
}
fetchSportsData()
console.log(sportsData)
My Api File
export const testApi = {
method: 'GET',
url: 'https://api-football-v1.p.rapidapi.com/v3/timezone',
headers: {
'X-RapidAPI-Key': '-------removed on purpose ---------',
'X-RapidAPI-Host': 'api-football-v1.p.rapidapi.com'
}
}
I am just setting the data using useState but it seems to repeatedly rerender whenever a value is stored. I've tried many roundabout ways but it seems to repeatedly request over and over again. Has anyone used API's from RapidAPI before?
While I don't know why useState will still repeatedly retrieve API data with axios, this is a workaround as commented by Sergey Sosunov.
On the React File
const [sportsData, setSportsData] = useState()
const fetchSportsData = () => {
axios.request(testApi).then((response) => {
setSportsData(response.data)
})
.catch((error) => {
console.log(error)
})
}
useEffect(()=> {
fetchSportsData()
},[])
On dev mode, the useEffect will run twice on load and depending on your API provider, this may mean calling the API twice on every load which may double your costs unnecessarily, this only happens in development mode as outlined in react documentation, what you can do is include a useRef variable.
const firstRender = useRef(false)
useEffect(()=>{
if (firstRender.current) {
fetchSportsData()
} else {
firstRender.current = true;
}
},[])
Remember that this code may not execute perfectly when in production as useEffect is only run once and the above code needs it to run twice in order to change the state of firstRender to true before retrieving the API call. This is just to help you lower your development costs.
I am aware this is a common question as I have spent the last two hours going through every answer and trying to get my state to update but nothing is working.
I am fetching text from a cms however on first load the state is undefined and my app crashes. However if I comment the line out, load the page and uncomment the line the correct values are displayed.
Here is some of the code I have tried.
The data i am hoping to get
[
{id:1},
{id:2},
{id:3},
{id:4},
]
import react, {useEffect, useState} from 'react'
import axios from 'axios'
const [carouselTitle, setCarouselTitle] = useState([])
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
await axios('api').then(
response => {
console.log(response.data)
setCarouselTitle(response.data)
console.log(carouselTitle)
})
};
return(
<h1>{carouselTitle[1].id}</h1>
)
console logging the data works fine but when i console log the state it does not work.
2ND METHOD I TRIED
useEffect(() => {
const fetchData = async () => {
const res = await axios('api');
const carouselTitleAlt = await res.data;
setCarouselTitle({ carouselTitleAlt });
console.log(carouselTitleAlt);
console.log(carouselTitle);
};
fetchData();
}, []);
Again console logging the const inside the useEffect displays the correct information but logging the state does not work.
Appreciate your responses or better ways of displaying the data.
setState is asynchronous : https://reactjs.org/docs/faq-state.html#why-doesnt-react-update-thisstate-synchronously
It means that you cannot expect to console.log the new state value the line after you called setCarouselTitle.
To log the new value, you could use another useEffect, with carouselTitle in the dependencies array, where you console.log(carouselTitle) :
useEffect(() => {
console.log(carouselTitle);
}, [carouselTitle]);
That said, your component should behave correctly, it will be refreshed when the state is updated.
In the JSX you should check that carouselTitle is not undefined (meaning that the request failed or is still pending) :
{carouselTitle && <H1>{carouselTitle[0].id}}
https://reactjs.org/docs/conditional-rendering.html#gatsby-focus-wrapper
First of all, if you pass an empty array for initial data to useState, you can't get any item in that array in here:
return(
<h1>{carouselTitle[1].id}</h1>
)
Because component returns first item of an array that has nothing. I prefer to you do it like this:
return(
<h1>{carouselTitle.length > 0 && carouselTitle[0].id}</h1>
)
And also based on this and official documentation, setState (and also setSomthing() using useState()) is asynchronous.
So state data doesn't show immediately after setting that.
You should trigger useEffect for run fetch function
useEffect(()=>{fetchData();},[carouselTitle])
I'm making a React dashboard that calls an API every minute for updates. Following the many answers in SO, I have this at the moment that sort of works:
const Dashboard = (props) => {
const [stockData, setStockData] = useState([]);
useEffect(() => {
//running the api call on first render/refresh
getAPIData();
//running the api call every one minute
const interval = setInterval(() => {
getAPIData()
}, 60000);
return () => clearInterval(interval);
}, []);
//the api data call
const getAPIData = async () => {
try {
const stdata = await DataService.getStockData();
setStockData(stdata);
}
catch (err) {
console.log(err);
}
};
However I keep getting browser warning
React Hook useEffect has a missing dependency: 'getAPIData'. Either include it or remove the dependency array
Is this a cause for concern (e.g. causing memory leaks)?
Ive tried to fix it:
It doesnt seem possible to not use useEffect (use setInterval directly)
If I remove dependency or put stockData as dependency for useEffect,
I'll see the API call being made every second, so I assume thats not
right.
If I put the api data call block directly in useEffect, the page wont
have the data shown when it loads the first time/or the page refreshed and I have to wait
for a minute.
I found several references on the issue such as
here
and
here
but I couldn't comprehend it given that I've only started using React
a month ago.
Appreciate any help for this!
You can resolve this issue in multiple way:
You can put getApiData in useEffect direct and use it...
You can use useCallBack, useEffect is go to re-render and make mempry leeek issue since every time react render Dashboard its re-create the getAPIData, you can prevent this case by using useCallBack, and you must make sure about dependency, just you need to put what you need...for example:
I have two other useEffects in my code that fetch data, update a state hook, and allow access to all data properly in other components/same component.
I have one useEffect, however, that's grabbing data from an api fetch that uses an authorization header. I've done this before, with no issue, but on the one in question, it gets the data, updates my state hook, and I can even console.log it out in another component, but the moment I try to do something with it, it gives me an error.
UseEffect:
useEffect(() => {
const claimStudents = async() => {
const url = "http://127.0.0.1:8000/api/TaughtStudents";
const result = await axios(url, {
headers: {
Authorization: `JWT ${localStorage.getItem("token")}`,
}
})
getState.updateClaimedStudents(result.data)
}
claimStudents()
}, [])
Error:
console error
Component that is calling my state data:
function ClaimedStudents() {
const getState = useContext(UserContext)
console.log(getState.claimedStudents)
return (
<Card>
<Card.Text>
</Card.Text>
</Card>
);
}
export default ClaimedStudents;
Console log that shows my data loading, though with a delay:
Console.log showing data
I know it's something to do with promises and/or the data not delivering in the right time, but I have had little luck googling this issue for an answer.
Any insight or points in the right direction of where I can help fix this would be appreciated.
I think you need to add a request status observable, like isLoadingStudents, it should be true while fetching the data and false otherwise, then add a spinner or something in the component if the isLoadingStudents is true.
if (isLoadingStudents) {
return <Spinner/>
}
Or you can directly go with this:
Warning: You cannot do custom error handling
getState? (
getState.claimstudent.map(({student}=>{
<li> {student} </li>
}))
):(
<Loader />
)
So turns out I was a goof and did the wrong thing inside my useState() hook. Whoops me. It didn't fix my data being not accessible 100%, but I'm no longer getting the error.