React fetch sending request multiple times - reactjs

I'm trying fetching data in react. I call the fetch function only once but it's sending request multiple times but I don't know why. I looked other questions and tried their answers but none of them worked.
When I delete useEffect and leave the function alone, it sends a request once, but I think this is not the right way.
useEffect(() => {
fetchFunction();
}, [])
const fetchFunction =() => {
console.log("ldşsaşdlisaldi")
axios.get(
"someAPI",
{
headers: {
"Authorization" : localStorage.getItem("token")
},
credentials: 'include',
}
)
.then(res => res.json())
.then(
(result) => {
console.log(result)
setIsLoaded(true);
setTableData(result);
},
(error) => {
setIsLoaded(true);
setError(error);
}
)
}

Don't attempt to do any sort of "initial mount" check or "state" as this is considered anti-pattern. Don't try to "outsmart" React. The double-mounting is a way for the React.StrictMode component to help you see unexpected side-effects and other issues. You should implement a cleanup function to cancel any in-flight requests when the component unmounts. Use an abortController with the axios GET request.
Example:
useEffect(() => {
const controller = new AbortController(); // <-- create controller
fetchFunction({ controller }); // <-- pass controller
return () => controller.abort(); // <-- return cleanup function
}, []);
const fetchFunction = ({ controller }) => {
axios.get("someAPI", {
headers: {
"Authorization" : localStorage.getItem("token")
},
credentials: 'include',
signal: controller.signal // <-- pass signal to request
})
.then(res => res.json())
.then((result) => {
console.log(result);
setTableData(result);
})
.catch((error) => {;
setError(error);
})
.finally(() => {
setIsLoaded(true);
});
}
For more details and explanation see Fetching Data.

Related

React useEffect on [ ] adds duplicate items to the list [duplicate]

I'm trying fetching data in react. I call the fetch function only once but it's sending request multiple times but I don't know why. I looked other questions and tried their answers but none of them worked.
When I delete useEffect and leave the function alone, it sends a request once, but I think this is not the right way.
useEffect(() => {
fetchFunction();
}, [])
const fetchFunction =() => {
console.log("ldşsaşdlisaldi")
axios.get(
"someAPI",
{
headers: {
"Authorization" : localStorage.getItem("token")
},
credentials: 'include',
}
)
.then(res => res.json())
.then(
(result) => {
console.log(result)
setIsLoaded(true);
setTableData(result);
},
(error) => {
setIsLoaded(true);
setError(error);
}
)
}
Don't attempt to do any sort of "initial mount" check or "state" as this is considered anti-pattern. Don't try to "outsmart" React. The double-mounting is a way for the React.StrictMode component to help you see unexpected side-effects and other issues. You should implement a cleanup function to cancel any in-flight requests when the component unmounts. Use an abortController with the axios GET request.
Example:
useEffect(() => {
const controller = new AbortController(); // <-- create controller
fetchFunction({ controller }); // <-- pass controller
return () => controller.abort(); // <-- return cleanup function
}, []);
const fetchFunction = ({ controller }) => {
axios.get("someAPI", {
headers: {
"Authorization" : localStorage.getItem("token")
},
credentials: 'include',
signal: controller.signal // <-- pass signal to request
})
.then(res => res.json())
.then((result) => {
console.log(result);
setTableData(result);
})
.catch((error) => {;
setError(error);
})
.finally(() => {
setIsLoaded(true);
});
}
For more details and explanation see Fetching Data.

React - API call running multiple times

I'm writing a react app that fetches data from two different APIs. In the code below, I call the two APIs then console.log the response. After npm start, in the console, I see that the response has been console.logged a few times. I don't know why it's doing this and it's causing issue with the app's behavior. It's receiving the data from aws dynamoDB tables.
function App() {
const [ response , setResponse ] = useState();
const [ btnText , setbtnText ] = useState('Get Data');
const [ details , setDetails ] = useState();
async function fetchData() {
try {
await fetch('url hidden' , {
method: 'POST',
header: {'Content-Type': 'application/json'},
}).then(res => res.json())
.then(res => setResponse(res))
await fetch('url hidden' , {
method: 'POST',
header: {'Content-Type': 'application/json'},
}).then(res => res.json())
.then(res => setDetails(res))
} catch (error) {
console.log(error);
};
}
console.log(response)
return (
<div className="container">
<header className='header'>
<button onClick={fetchData}>{btnText}</button>
</header>
<Summary response={response} details={details} />
</div>
);
}
I also tried useEffect to fetch data as soon as app loads, but it's doing the same thing.
useEffect(() => {
try {
Promise.all([
fetch('url hidden' , {
method: 'POST',
header: {'Content-Type': 'application/json'},
}).then(res => res.json()).then(res => setResponse(res)),
fetch('url hidden' , {
method: 'POST',
header: {'Content-Type': 'application/json'},
}).then(res => res.json()).then(res => setDetails(res)),
]);
}
catch(err) {
console.log(err);
}
} , [])
this image shows the response after clicking the button only once
When you use console.log in the the functional component you will get that console.log each time the component rerenders. And that happens for example each time you set a new state.
You could just move the console.log inside the fetch function.
Or you just console.log the values in useEffect if they change. Like in the example below.
I also refactored the fetchData function to use async await and Promise.all more efficient.
Also you were missing an "s" for "headers" for the fetch method.
async function fetchData() {
try {
const [response, details] = await Promise.all([
(
await fetch("url hidden", {
method: "POST",
headers: { "Content-Type": "application/json" },
})
).json(),
(
await fetch("url hidden", {
method: "POST",
headers: { "Content-Type": "application/json" },
})
).json(),
]);
setResponse(response);
setDetails(details);
} catch (error) {
console.log(error);
}
}
useEffect(() => {
fetchData();
}, []);
useEffect(() => {
console.log(response, details);
}, [response, details]);
If you move console.log(response) inside fetchData then you will get exact information about how many times handler executes, and it really should be only once, on click.
With your approach you moved log in component body and this will cause log to execute each time element rerenders - probably 3 times: one log initially when element is loaded in DOM, and other 2 when you set states inside handler.

useEffect: How to put data in the state in order

I'd like to ask how to retrieve data through use Effect.
The flow I want is as follows.
First, I want to get the 'cards' state, fill the cards with data, and then fill the data through the cardsPromises after that.
But my code couldn't get cards and wordAll, and the empty value came out.
I think it's because the cards are still empty, but I don't know how to operate in order.
Please tell me how to do it.
const [wordAll, setWordAll] = useState([]);
const [cards, setCards] = useState([]);
useEffect(() => {
axios
.get("http/api/words/", {
headers: {
Authorization: cookies.token,
},
})
.then((response) => {
setCards(response.data);
})
.catch((error) => {
console.log(error);
});
const cardsPromises = cards.map((contents) =>
axios.get(
`http/api/words/detail_list/?contents=${contents.contents}`,
{
headers: {
Authorization: cookies.token,
},
}
)
);
console.log("cards", cards);
Promise.all(cardsPromises)
.then((response) => {
console.log("resp", response.data);
setWordAll(response.data);
})
.catch((error) => {
console.log("err==>", error);
});
}, []);
You are correct, cards array is still empty in the useEffect callback when the fetching the data. I suggest converting to async/await and waiting for the first fetch to resolve and using that value of cards for the fetching of the rest of the data.
const [wordAll, setWordAll] = useState([]);
const [cards, setCards] = useState([]);
useEffect(() => {
const fetchData = async () => {
try {
const{ data: cards } = await axios.get(
"http/api/words/",
{
headers: {
Authorization: cookies.token,
},
},
);
setCards(cards);
const cardsPromises = cards.map((contents) =>
axios.get(
`http/api/words/detail_list/?contents=${contents.contents}`,
{
headers: {
Authorization: cookies.token,
},
}
);
);
const wordAllResponse = await Promise.all(cardsPromises);
const wordAll = wordAllResponse.map(({ data }) => data);
setWordAll(wordAll);
} catch (error) {
// handle any errors, rejected Promises, etc..
}
};
fetchData();
}, []);
Wrap your 2nd axios call inside a function, and call it after 1st axios call returns.
useEffect(() => {
const getWords = (cards) => {
const cardsPromises = cards.map((contents) =>
axios.get(
`http/api/words/detail_list/?contents=${contents.contents}`,
{
headers: {Authorization: cookies.token}
}
)
);
Promise.all(cardsPromises)
.then((response) => {
setWordAll(response.data);
})
.catch((error) => {
console.log("err==>", error);
});
})
axios
.get("http/api/words/", {
headers: { Authorization: cookies.token },
})
.then((response) => {
const cards = response.data;
setCards(cards);
getWords(cards);
})
.catch((error) => {
console.log(error);
});
}, [])
Now dependency chain is clearer.

Updating useEffect api call url with user inputs

I'm working on a project, where I need to update a datachart with user inputted dates. I'm having trouble on how to update the url inside the useEffect hook. Here's my relevant code:
const finalUrl =`${apiUrl}id=${id}&timing=${time}&start=${finalStart}&end=${finalEnd}`;
console.log(finalUrl);
useEffect(() => {
axios
.get<AxiosResponse>(finalUrl, {
headers: {
"Content-Type": "application/json"
}
})
.then(response => {
setData(response);
})
.catch(error => {
console.log(error);
});
}, []);
console.log(data);
Everything looks good until i get to the axios call. I cannot get useEffect to use the updated url. Logging the response data just gives the same every time. All values inside "finalUrl" are coming from the user.
I'm going to assume that apiUrl and id never change, but that all the other things you're using in the API URL are inputs from the user.
If so, you need to rebuild the URL in the useEffect callback, and make the callback dependent on those user inputs, like this:
useEffect(() => {
const finalUrl =`${apiUrl}id=${id}&timing=${time}&start=${finalStart}&end=${finalEnd}`;
axios
.get<AxiosResponse>(finalUrl, {
headers: {
"Content-Type": "application/json"
}
})
.then(response => {
setData(response);
})
.catch(error => {
console.log(error);
});
}, [time, finalStart, finalEnd]);
The callback will be called again when time, finalStart, or finalEnd change.
Note that you also need to disregard or cancel previous requests when the dependencies change, even if the request hasn't been completed yet. I don't use axios but as I understand it has a "cancel/cancellation token" you can use for doing that. Here's what it would look like with fetch, which uses AbortController:
useEffect(() => {
const finalUrl =`${apiUrl}id=${id}&timing=${time}&start=${finalStart}&end=${finalEnd}`;
// Create the controller so we can cancel the request
const controller = new AbortControlller();
// Pass `signal` to fetch vvvvvvvvvvvvvvvvvvvvvvvvvvv
fetch<DataType>(finalUrl, {signal: controller.signal})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
return response.json();
})
.then(setData)
.catch(error => {
console.log(error);
});
// Return a cleanup callback
return () => {
// Cancel the request since its response would be out of date
controller.abrt();
};
}, [time, finalStart, finalEnd]);
console.log(data);

Undefined 404 and React Hook useEffect has a missing dependency

I have a super cute site for flower-fans where you can find a profile of a flower (a mock api), read some info and put a note on each and every flower. Though, I'm not able to make the note stick anymore. So frustrating as it worked a while ago. I have updated the dependencies and also the settings for deploying on Netlify. In Postman I get the same message as in the console, which is not found 404.
I get a message in Terminal that the React Hook useEffect has a missing dependency (flowerId) too.
Down below you'll see the error message and here is a link to my deployed site:
https://flowerinspoapi.netlify.app/
Error message from Console
GET https://flowerinspoapi.netlify.app/flowers/undefined 404
Code from Flowerinfo.js
// Fetching the comments for the flowers
const url = "https://flowers-mock-data.firebaseio.com/comments/TheresaUlwahn"
export const FlowerInfo = () => {
const { flowerId } = useParams()
const [flower, setFlower] = useState([])
const [flowerMessages, setFlowerMessages] = useState([])
const [postedMessage, setPostedMessage] = useState("")
// Fetching the ID of the flowers
useEffect(() => {
fetch(`https://flowers-mock-data.firebaseio.com/flowers/${flowerId}.json`)
.then((res) => res.json())
.then((json) => {
setFlower(json)
})
}, [flowerId])
// Fetching the messages
useEffect(() => {
fetch(`https://flowers-mock-data.firebaseio.com/comments/TheresaUlwahn/${flowerId}.json`)
.then((res) => res.json())
.then((json) => {
console.log('All messages for the flower: ', json)
if (json !== null) {
setFlowerMessages(json)
}
})
}, [postedMessage])
const handleFormSubmit = (flowerId, message) => {
// console.log('POST THIS MESSAGE: ', message, 'FOR THE FLOWER: ', flowerId);
fetch(url + `/${flowerId}/.json`, {
method: "POST",
body: JSON.stringify({ message }),
headers: { "Content-Type": "application/json" }
})
.then(() => {
console.log('posted !')
// window.location.reload();
setPostedMessage(message)
})
.catch(err => console.log("error:", err))
}
var result = Object.keys(flowerMessages).map(function (key) {
return [key, flowerMessages[key]];
});

Resources