I'm beginner with React. I have 2 different cases where I'm using React Hooks which I cannot receive the data from my local API properly.
Case 1:
export const RegisterT = () => {
const [test, setTest] = useState()
const addrState = {}
axios.get('http://127.0.0.1:3333/states', { addrState })
.then(res => {
setTest(res.data)
console.log(test)
})
...
}
It works with the state test displaying correctly the content from the API but I don't know why/how the Axios continues calling the API infinity - endless. (Ps: the very first call it returns undefined, then the next ones it works) What am I doing wrong?
To fix this I've tried to use useEffect like this (Case 2):
export const RegisterT = () => {
const [test, setTest] = useState()
const addrState = {}
useEffect(() => {
axios.get('http://127.0.0.1:3333/states', { addrState })
.then(res => {
setTest(res.data)
console.log(test);
})
}, [])
...
}
Now the Axios works only once but no data is coming from the API. Maybe I should use async/wait for this case but I cannot make it work. Does anyone know how to fix that (Case 1 or/and Case 2)?
Thanks.
Updating the state is an asynchronous operation. So the state is not really updated until the next time the component gets rendered. If you want to capture the correct state, you can either console.log(res.data) or wrap that inside the useEffect hook with test as dependency.
export const RegisterT = () => {
const [test, setTest] = useState()
const addrState = {}
// effect only runs when component is mounted
useEffect(() => {
axios.get('http://127.0.0.1:3333/states', { addrState })
.then(res => {
setTest(res.data);
});
}, []);
// effect runs whenever value of test changes
useEffect(() => {
console.log(test);
}, [test]);
}
That way it is guaranteed that the console.log runs when the value of test is updated.
Also the reason the API request is invoked once is you have not mentioned anything in the dependency array. [] empty dependency array runs the effect when the component is mounted for the first time.
async/await is just a wrapper around Promise object. So they would behave similarly.
The solution with useEffect is good. If you don't use it each render will call the request. This is the same if you put there console.log with any information. The reason why you don't see the data in the useEffect is that the value of the state is not updated in current render but in the next which is called by setter of the state. Move the console.log(test); after useEffect to see the data. On init it will be undefined but in the next render, it should contain the data from the request.
Related
Hi I am learning React Hooks I know useEffect when used without any dependency array should run after every render but what about render itself does it happen every time a state changes because I am trying following code where I am rending a static h1 to the DOM and in my useEffect I am using a fetch to call a json file on the success of the fetch I am setting a state loaded to true from false I have written this example for the learning purpose only I was expecting that this should have caused a infinite loop because useEffect without dependency array should run after every render and I am calling set state which according to me should force the render to happen but It only run the useEffect twice but
when I am using the other example where I am using useState create a name state and in the useeffect without dependency arry I am changing the name state with Math.random it is behaving as expected causing infinite loop what I understand is useState does not cause render state to happen untill the value of the state changed as in the first example the state value only changed once from false to true and in name example it is random so changes every time
Just need a to know is my understanding correct and also if you can share where I can learn these Kind of stuff as I did not find this on the react doc.
example 1 with fetch
`
const App = () => {
// const [name, setName] = useState('sumit');
const [loaded, setLoaded] =useState(false);
useEffect(() => {
console.log('sumit');
fetch('./option.json').then((res) => {
console.log(res);
return res.json();
}).then((res) => {
console.log(res);
setLoaded(true);
})
// setName(Math.random());
})
return (
<h1>sumit</h1>
);
}
example 2 with name
const App = () => {
const [name, setName] = useState('sumit');
// const [loaded, setLoaded] =useState(false);
useEffect(() => {
console.log('sumit');
/* fetch('./option.json').then((res) => {
console.log(res);
return res.json();
}).then((res) => {
console.log(res);
setLoaded(true);
}) */
// setcount(count+1);
setName(Math.random());
})
return (
<h1>sumit</h1>
);
}
`
I expected to get the url with category=business,but the web automatically reset my state to the url that dosent have the category.I dont know the reason behind
let {id}=useParams()
const [newsurl,setNewsurl]=useState(()=>{
const initialstate="https://newsapi.org/v2/top-headlines?country=us&apiKey=c75d8c8ba2f1470bb24817af1ed669ee"
return initialstate;})
//console.log(id);
const [articles, setActicles] = useState([]);
useEffect( ()=>{
if(id === 2)
console.log("condition")
setNewsurl("https://newsapi.org/v2/top-headlines?country=de&category=business&apiKey=c75d8c8ba2f1470bb24817af1ed669ee")},[])
useEffect(() => {
const getArticles = async () => {
const res = await Axios.get(newsurl);
setActicles(res.data.articles);
console.log(res);
};
getArticles();
}, []);
useEffect(() => {
console.log(newsurl)
// Whatever else we want to do after the state ha
s been updated.
}, [newsurl])
//return "https://newsapi.org/v2/top-headlines?country=us&apiKey=c75d8c8ba2f1470bb24817af1ed669ee";}
return (<><Newsnavbar />{articles?.map(({title,description,url,urlToImage,publishedAt,source})=>(
<NewsItem
title={title}
desciption={description}
url={url}
urlToImage={urlToImage}
publishedAt={publishedAt}
source={source.name} />
)) } </>
)
one more things is that when i save the code the page will change to have category but when i refresh it ,it change back to the inital state.Same case when typing the url with no id.May i know how to fix this and the reason behind?
Setting the state in React acts like an async function.
Meaning that the when you set the state and put a console.log right after it, it will likely run before the state has actually finished updating.
You can instead, for example, use a useEffect hook that is dependant on the relevant state in-order to see that the state value actually gets updates as anticipated.
Example:
useEffect(() => {
console.log(newsurl)
// Whatever else we want to do after the state has been updated.
}, [newsurl])
This console.log will run only after the state has finished changing and a render has occurred.
Note: "newsurl" in the example is interchangeable with whatever other state piece you're dealing with.
Check the documentation for more info about this.
setState is an async operation so in the first render both your useEffetcs run when your url is equal to the default value you pass to the useState hook. in the next render your url is changed but the second useEffect is not running anymore because you passed an empty array as it's dependency so it runs just once.
you can rewrite your code like the snippet below to solve the problem.
const [articles, setActicles] = useState([]);
const Id = props.id;
useEffect(() => {
const getArticles = async () => {
const newsurl =
Id === 2
? "https://newsapi.org/v2/top-headlines?country=de&category=business&apiKey=c75d8c8ba2f1470bb24817af1ed669ee"
: "https://newsapi.org/v2/top-headlines?country=us&apiKey=c75d8c8ba2f1470bb24817af1ed669ee";
const res = await Axios.get(newsurl);
setActicles(res.data.articles);
console.log(res);
};
getArticles();
}, []);
I have a handleRating function which sets some state as so:
const handleRating = (value) => {
setCompanyClone({
...companyClone,
prevRating: [...companyClone.prevRating, { user, rating: value }]
});
setTimeout(() => {
handleClickOpen();
}, 600);
};
I think also have a function which patches a server with the new companyClone values as such:
const updateServer = async () => {
const res = await axios.put(
`http://localhost:3000/companies/${companyClone.id}`,
companyClone
);
console.log("RES", res.data);
};
my updateServer function gets called in a useEffect. But I only want the function to run after the state has been updated. I am seeing my res.data console.log when I load my page. Which i dont want to be making reqs to my server until the comapanyClone.prevRating array updates.
my useEffect :
useEffect(() => {
updateServer();
}, [companyClone.prevRating]);
how can I not run this function on pageload. but only when companyClone.prevRating updates?
For preventing function call on first render, you can use useRef hook, which persists data through rerender.
Note: useEffect does not provide the leverage to check the current updated data with the previous data like didComponentMount do, so used this way
Here is the code example.
https://codesandbox.io/s/strange-matan-k5i3c?file=/src/App.js
I Got Stuck in an infinite loop in react.js. How to resolve this?
useEffect(() => {
fetch("https://react-http-d55a9-default-rtdb.firebaseio.com/todo.json")
.then((response) => {
return response.json();
})
.then((data) => {
console.log(data);
setUsersList((prev) => [...prev]); //cause of infinite loop
});
}, [usersList]);
You are having an infinite loop because your useEffect array of dependencies has usersList on it and at the same time you are updating this variable inside your useEffect function. So your useEffect runs when the component mounts which updates your usersList which makes the useEffect run again which again updates your usersList which makes it run again and so on...
To fix this, remove usersList from the array of dependencies and have an empty array instead: []. If you do this your useEffect will run once, when your component mounts.
The dependency list passed to useEffect determines when the effect should run again. The infinite loop is happening because this effect causes usersList to change, which triggers the effect to run again.
Since this effect doesn't use any other variables, it doesn't need anything in its dependency list:
useEffect(() => {
fetch(...)
// ...
}, []); // leave this empty, so the effect only runs when the component mounts
If your URL depended on a prop or something else, then you want it in the dependency list:
useEffect(() => {
fetch(`https://example.com/todo/${props.id}`)
.then(...)
// Since the URL depends on the id prop, the effect should re-run if it changes
}, [props.id]);
According to question asked, you want the userList to be watched everytime it updates. What we can do is define one more state variable as mentioned in the code as isFetched or if you are using redux you can put that over there, because if we just watch the userList variable then it caughts up in infinite loop as setting the userList is happening in useEffect itself. With the help of isFetched, we can manage when to call the api and whenever the flag is false it calls the api.
Right now in the code i have put one more state variable as setCount, as i didn't know how many times you want to call your api. So you can put your condition there and stop the call when your condition satisfies.
function App() {
const [userList, setUserList] = useState([]);
const [isFetched, setIsFetched] = useState(false);
const [, setCount] = useState(3);
const callApiPending = useCallback(()=>{
fetch("https://react-http-d55a9-default-rtdb.firebaseio.com/todo.json")
.then((response) => response.json())
.then((json) => {
setUserList((prev) => [...prev, ...json]);
setCount((cnt) => {
if(cnt - 1 === 0){
setIsFetched(true);
}
return cnt - 1;
});
});
}, []);
useEffect(() => {
if (!isFetched) {
callApiPending();
}
}, [isFetched, userList, callApiPending]);
return <div>Executing....</div>;
}
You ran fetch if usersList changes. Even if userList content is the same as previous content, javascript interpret as it changed. Try this one.
[1,2,3] == [1,2,3]
may return false. You can use a flag which is used to check whether or not to get data instead of using array.
just starting to learn hooks here.
I am getting data from firestore and trying to set it to state using hooks. when I uncomment the line doing so, I get stuck in an infinite loop. no error, but the console goes crazy with logging the state thousands of times.
Let me know if you need more info!
function Lists(props) {
const [lists, setLists] = useState([])
const [loading, setLoading] = useState(true)
useEffect(() => {
const subscriber =
firestore().collection('users').doc(props.user).collection('lists')
.onSnapshot(QuerySnapshot => {
const items = []
QuerySnapshot.forEach(documentSnapshot => {
items.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
//setLists(items)
setLoading(false)
console.log(lists)
})
})
// unsubscribe from firestore
return () => subscriber();
})
//rest of func..
this issue happens becauase useEffect gets called over and over again. useEffect is like componentDidMount and componentDidUpdate if you are familiar with React class components.
so whenever you set the state inside the useEffect, you trigger an update, and then, useEffect gets called again, and thus the infinite loop.
to fix this, useEffect accepts a extra argument, which is an array of dependancies, which indicates that this useEffect call should only re-executed whenever a change happens to one of its dependancies. in your case you can provide an empty array, telling react that this useEffect should only be called one time.
useEffect(() => {
const subscriber =
firestore().collection('users').doc(props.user).collection('lists')
.onSnapshot(QuerySnapshot => {
const items = []
QuerySnapshot.forEach(documentSnapshot => {
items.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
//setLists(items)
setLoading(false)
console.log(lists)
})
})
// unsubscribe from firestore
return () => subscriber();
}, []) // <------------ the second argument we talked about