Can we pass an asynchronous function (example fetchData() ) in a dependancy array of useEffect of a react component ? example :
useEffect(()=>{fetchData().then(data=>{ //do something with data like setting state
)}},[fetchData])
Yes this is fine. Just be aware that a new, potentially overlapping, request will be made every time fetchData changes. If you only want to make this request once use an empty dependency array.
yes for sure but you need to pass empty dependency to avoid overlapping or pass an state array dependency:
useEffect(() => {
async function fetchMyAPI() {
let response = await fetch('api/data')
response = await response.json()
dataSet(response)
}
fetchMyAPI()
}, [])
Related
This is my code which sends a GET request to my backend (mySQL) and gets the data. I am using useState to extract and set the response.data .
const baseURL = 'http://localhost:5000/api/user/timesheet/13009';
const [DataArray , setDataArray] = useState([]);
axios.get(baseURL).then( (response)=>{
setDataArray(response.data);
});
But useState keeps on sending the GET request to my server and I only want to resend the GET request and re-render when I click a button or execute another function.
Server Terminal Console
Is there a better way to store response.data and if not how can I stop automatic re-rendering of useState and make it so that it re-renders only when I want to.
As pointed out in the comments, your setState call is triggering a re-render which in turn is making another axios call, effectively creating an endless loop.
There are several ways to solve this. You could, for example, use one of the many libraries built for query management with react hooks, such as react-query. But the most straightforward approach would be to employ useEffect to wrap your querying.
BTW, you should also take constants such as the baseUrl out of the component, that way you won’t need to include them as dependencies to the effect.
const baseURL = 'http://localhost:5000/api/user/timesheet/13009';
const Component = () => {
const [dataArray , setDataArray] = useState([]);
useEffect(() => {
axios.get(baseURL).then( (response)=>{
setDataArray(response.data);
});
}, []);
// your return code
}
This would only run the query on first load.
you have to wrap your request into a useEffect.
const baseURL = 'http://localhost:5000/api/user/timesheet/13009';
const [DataArray , setDataArray] = useState([]);
React.useEffect(() => {
axios.get(baseURL).then((response)=>{
setDataArray(response.data);
})
}, [])
The empty dependency array say that your request will only be triggered one time (when the component mount). Here's the documentation about the useEffect
Add the code to a function, and then call that function from the button's onClick listener, or the other function. You don't need useEffect because don't want to get data when the component first renders, just when you want to.
function getData() {
axios.get(baseURL).then(response => {
setDataArray(response.data);
});
}
return <button onClick={getData}>Get data</button>
// Or
function myFunc() {
getData();
}
I'm trying to build a Update profile form where a user can change his details and onSubmit the new data will be sent to PUT/users end point to update the details in the database. The form will have the old values pre filled (gotten through a GET request to the same /users endpoint). To access this endpoint we also need to send a basic auth header with email and password. Now I'm making two fetch requests one to get the existing details and one to PUT the new details. My first GET request is successfully made and I can see the data prefilled but my second POST request doesn't work. What am I doing wrong?
Here is how my code looks. https://codesandbox.io/s/dawn-breeze-itinq?file=/src/App.js
const getUsers = async () => {
let myHeaders = new Headers();
myHeaders.set('Authorization', 'Basic ' + credentials);
const requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow',
};
try {
const response = await fetch(`${APIlink}/users`, requestOptions);
const result = await response.json();
setData(result);
} catch (err) {
setErr('Incorrect Password. Please Retry.');
}
};
useEffect(() => {
getUsers();
}, []);
You useEffect gets called on Every render as you have not mentioned any dependency array. So what happens in your case is,
your component renders --> useEffect gets called --> make api call --> you set state --> component re-renders --> useEffect gets called --> make api call and this cycle continues forever .
useEffect(() => {
....
}); => this useEffect will trigger on first render and every re-render
useEffect(() => {
...
}, []); => this will trigger only for the first time when the component is mounted
useEffect(() => {
...
}, [value]); => triggers on first render and whenever the value changes
If you are familiar with lifecycle methods in class based components, you usually do API calls in componentDidMount lifecycle method. This method is called only once after the first render. More on this: https://reactjs.org/docs/react-component.html#componentdidmount
In your code, you are using useEffect which is more or less like the componentDidUpdate lifecycle method, which is called on every render. this is why,
The page loads and I can see an endless string of GET requests in the network tab.
More on this: https://reactjs.org/docs/react-component.html#componentdidupdate
The solution would be to make your useEffect hook behave like a componentDidMount lifecycle method, which is essentially telling the useEffect hook to run only once.
If we examine a hook, it is made up of two components:
Callback function (required)
Dependency list of props (optional)
which looks like this:
useEffect(Callback, [Dependencies]);
An example would be:
useEffect(() => {
console.log(props.color)
}, [props.color]);
So essentially we are saying that whenever the prop color is changed, we want to log the value of color.
Seeing as how this is, if we pass an empty array of dependencies, the useEffect hook will only run once and that is what you should do. Do note that if a dependencies array is not passed, it will still behave like componentDidUpdate lifecycle method i.e it will be executed on every render.
Solution if you just skipped to this part is:
Pass an empty dependencies list in your useEffect hook.
More on hooks:
https://reactjs.org/docs/hooks-effect.html#explanation-why-effects-run-on-each-update
https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
I'm new to react. So I'm developing a application which gets data from a api. So I used this piece of code to get data from a api.
let [jsonData,setJsonData]=useState([]);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(apiURL);
const json = await response.json();
setJsonData(json.components.map(function(item){
return item;
}))
} catch (error) { }
}
fetchData();
}, [])
const [table1,table2,pieChart]=jsonData;
console.log(table1,table2,pieChart)
This is the problem. When I run this I get a output like this.
In this why there are two outputs in the first call and in first output why I get 3 undefined things.I just need to get the only those JSON data in the first call.How do I get the required data only and not to get those undefined things.
Thanks in advance.
You can't avoid the fact that your fetch function is asynchronous, but you can do something like this:
if (!jsonData.length) return <div>loading...</div>;
You are doing an asynchronous request in your useEffect function, which means that your fetch function will run parallel to the current code but it will finish sometime after. So the first time you console.log those values the useEffect has not finished yet. At the end of your useEffect you used the setJsonData, which will make the component re-render with the updated state, hence this is why you see the second console.log with the correct values at last.
I've been having a difficult time updating state inside of my React application lately using the useState hook.
If I define my state in a provider as -
const [session, setSession] = useState({});
const [sessionId, setSessionId] = useState(0);
And then try to set it using the setSession
setSession(response.data);
It always comes back as the default value. This all happens inside of the provider component - i.e. I'm trying to access the information within other functions in that same provider.
However, if I store that data in localStorage, for example, I have no issues accessing it whatsoever.
localStorage.setItem("session", JSON.stringify(response.data));
I've verified that the information coming from the server is an object, and that the correct data. There's no errors or promises, just the object containing the response. If I put the snippet the setSession(response.data) and localStorage.setItem("session", JSON.stringify(response.data)) next to each other, the setSession leaves the session value as {} whereas setting the local storage works perfectly. Both are using the same API response, same data
// This is the method on my component that I'm trying to use to update the state
const updateStateAfterSessionInitialization = async data => {
setSession(data)
localStorage.setItem("session", JSON.stringify(data));
setSessionId(data.id);
// both of these log a value of `{}` and `0` despite the values being set above
console.log(session)
console.log(sessionId)
closeStartSessionModal();
// If I redirect my application like this, it works fine. The ID is the correct value being returned by the server
window.location = "/#/reading-sessions/" + data.id;
}
// All of this code below is wrapped in a function. None of this code is being executed at the top level
let response = await axios({
method: method,
data:data,
url: url,
headers: headers
});
await updateStateAfterSessionInitialization(response.data);
Literally all of the data is working perfectly fine. The server responds with the correct data, the correct data is stored the session in local storage. If I redirect using the ID from the object from the server, it works fine. But if I try to update the state of the component and access the state properly, it just just doesn't work, and as a result I'm having to try to find ways of working around setting the state.
Is there something that I'm misunderstanding here?
The code that I'm working with is here - https://github.com/aaronsnig501/decyphr-ui/commit/ef04d27c4da88cd909ce38f53bbc1babcc3908cb#diff-25d902c24283ab8cfbac54dfa101ad31
Thanks
The misunderstanding you have here is an assumption that state updates will reflect immediately which is incorrect
State update is async and will only refect in the next render cycle. If you try to update state and log it in the next line, you wouldn't see and updated state
// This is the method on my component that I'm trying to use to update the state
const updateStateAfterSessionInitialization = async data => {
setSession(data)
localStorage.setItem("session", JSON.stringify(data));
setSessionId(data.id);
// both of these log a value of `{}` and `0` despite the values being set above
console.log(session) // This is expected to log previous value
console.log(sessionId) // This is expected to log previous value
closeStartSessionModal();
window.location = "/#/reading-sessions/" + data.id;
}
Now localStorage is synchronous and hence its update is reflected immediately
If you wish to see if the update to state was done correctly you could write a useEffect that depends on it
useEffect(() => {
console.log('State session/sessionId updated');
}, [session, sessionId])
Now depending on what you are trying to achieve you would need to modify your code in line with the above statement that state update calls are asynchronous.
Also setSession doesn't return a promise so you can't just use async await with it. You need to make use of useEffect to take an action on state update
For Example:-
import React, { useState, useEffect } from "react";
import axios from "axios";
function App() {
const [data, setData] = useState([]);
useEffect(() => {
const fetchData = async () => {
const result = await axios("https://jsonplaceholder.typicode.com/posts");
console.log(result.data);
setData(result.data);
};
fetchData();
}, []);
return (
<ul>
{data.map(res => (
<li>{res.title}</li>
))}
</ul>
);
}
export default App;
Check your type of response.data and defined the types
array - []
objects - {}
string - ""
number - 0 in useState
setState is async, so new value will apply on the next rerender not in the next line.
I am attempting to setState in a React component using a function callback which is now the recommended way.
It is in the componentDidMount and when I get my jobs data back I need to update the state.
It works when I set it directly but I have attempted may functions callbacks and cannot get it to work.
Sample code provided below with one of my many attempts.
async componentDidMount(){
const jobs = await loadJobs();
this.setState({jobs});
//this.setState((prevState, jobs) => {return {jobs: [prevState,...jobs]}})
}
What is the correct syntax?
You only need to make use of functional setState when you want to update current state based on prevState, in your case it seems like you just want to set the state jobs, you would simply write
this.setState({jobs});
However if you want to use functional setState, you would still write
this.setState((prevState) => {
return {jobs};
})
You need to update the jobs by getting value from prevState and appending the result to the previous state, you would do it like
async componentDidMount(){
const jobs = await loadJobs();
this.setState((prevState) => {
return {jobs: [...prevState.jobs, ...jobs]}
})
}