React Hook UseEffect create duplicate objects in setState - reactjs

I got a problem with a component. I make a n number calls to an external API and then get back values inside an object. I want to add each of this object return in the state. However, each object is duplicate in my state. I try to put conditions but nothing work. I just started with React and Node so I'm sorry if the solution is obvious...
//Id for the API calls
const Ref = ['268', '294']
const nbreLiens = Ref.length
//For each Id we make an API call, get the response, and set the state without erasing the previous value
Ref.map(lien=>{
const Tok = localStorage.getItem('token');
Axios.get("http://localhost:3001/liens/sup/"+Tok+"/"+lien).then((Response) => {
//We shouldn't get more entries that the number of Id's
if (test.length<nbreLiens){
setTest(test=>[...test,Response.data])
}
})
})
}, [])

Try resolving all the API call Promises first, then add the resposes to the state.
(Im not handling errors)
//Id for the API calls
const Ref = ['268', '294']
useEffect(() => {
const getElements = async (idsToGet) => {
const Tok = localStorage.getItem('token');
const allPromises = idsToGet.map(id => Axios.get("http://localhost:3001/liens/sup/" + Tok + "/" + id))
const dataElements = await Promise.all(allPromises)
const responseDataElements = dataElements.map(response => response.data)
setTest(() => responseDataElements)
}
getElements(Ref);
}, [])

Seems to me that your statement if (test.length<nbreLiens){ will not work as you are expecting.
In resume: test.length will always be the same even after calling setTest inside this function.
From react docs:
In React, both this.props and this.state represent the rendered
values, i.e. what’s currently on the screen.
Calls to setState are asynchronous - don’t rely on this.state to
reflect the new value immediately after calling setState. Pass an
updater function instead of an object if you need to compute values
based on the current state (see below for details).
You can read more about this at: https://reactjs.org/docs/faq-state.html

Related

usestate not setting the data

i am using usestate for transfer data. but ufotunately it not quite work.
here is my code:
const [totCons, settotCons] = useState(null)
useEffect(() => {
// declare the async data fetching function
const fetchData = async () => {
// get the data from the api
const data = await fetch('https://piscons2.vercel.app/ConsPiscTotCons');
// convert the data to json
const json = await data.json();
// set state with the result
settotCons(json);
console.log(json)
console.log(totCons)
}
// call the function
fetchData()
// make sure to catch any error
.catch(console.error);;
}, [])
as you can see on image the json return data but the totCons return null.
i did set it settotCons(json)
Updated state will not be available to the state value immedieately.
The react setState is asynchronous, but thats not the only reason for this behaviour. The reason is a closure scope around an immutable const value.
Both props and state are assumed to be unchanging during 1 render.
Treat this.state as if it were immutable.
You can use useEffect to create the sideeffects for totCons
useEffect(() => {
// action on update of totCons
}, [totCons]);
try doing console.log(totCons) outside useEffect.
you will not get the updated value in next line.
you will get the updated value in next render

React useEffect with Stripe - get client secret asynchronously

I've been plopped into the middle of a react project with no prior knowledge so I'm sure this a duplicate.
I want to use the StripeElements, and the react library is easy enough. I'm just not sure how to get the client secret from the server.
This is what I'm trying:
const stripePromise = loadStripe('stripe_key');
const StripeForm = () => {
const [stripeData, setStripeData] = useState({});
const getClientSecret = async () => {
const { data } = await api.get(`payment/stripe/get-client-secret/`);
return data
}
useEffect(() => {
getClientSecret().then(data => setStripeData(data))
}, [])
if(stripeData.clientSecret) {
return (
<QuoteContainer title={"Pay With Credit Card"}>
<SectionCard>
{stripeData.clientSecret}
</SectionCard>
</QuoteContainer>
);
}
return (<b>Loading</b>)
}
The route payment/stripe/get-client-secret/ returns an array with a key 'clientSecret'. This function is working correctly.
I just can't get the <b>Loading</b> text to be replaced with the QuoteContainer component once the promise is resolved.
If you want to rendering Loading component. You have to set falsy value for stripedData state. Because {} value is truthy. So I think the code is not rendering Loading component. The loading component is not rendered because there is no place to set it as a false value anywhere.
what AJAX library are you using to make the API call? Usually you need to call a function on the response object in order to access the data. For instance, if you are using fetch(), you need to call json() then access the response data.

How to fix multiple call fetch data in forEach() using React Hooks

In react Hooks, I am trying to fetch data from the API array but in the Foreach function, the API call causes infinity.
How to fix this?
const [symbols, setSymbols] = useState([]);
getPortfolioSymbolList(portfolio_name).then(data => data.json()).then(res => {
res.forEach((symbol_data)=>{
fetchPrice(symbol_data.symbol).then(price => {
setSymbols(price);
});
})
}
function fetchPrice(symbol){
const price = fetch(`api_url`)
.then(chart => chart.json())
return price;
}
Here, call fetchPrice() causes in infinite.
Setting the state will always cause a rerender
What happens in your code is the request is made and then the data is set causing a rerender. Then because of the rerender the request is made again and sets the state again and causes the rerender again.
If you have a request for data you probably want to put a React.useEffect so it only requests once.
React.useEffect(() => {
/* your data request and data set */
}, []); // the [] will only fire on mount.
Is is because your setSymbols call inside forEach makes component rerender (reload) - it means that all of your main component function is call again and again... getPortfolioSymbolList too. You have to use useEffect hook to resolve this problem. Your getPortfolioSymbolList() API call should be inside useEffect.
https://reactjs.org/docs/hooks-effect.html
PROBLEM
Your first symbol is updated in your API call, which triggers a re-render of the component calling the API call to go on an infinite loop.
SOLUTION
Wrap your API in your useEffect. The function inside your useEffect will only be called once. See useEffect docs here
You need to use for await of to loop asynchronously. forEach can't loop asynchronously. See for await of docs here
Update your symbols once all the data is collected.
function Symbols() {
const [symbols, setSymbols] = useState([]);
React.useEffect(() => {
async function fetchSymbols() {
const portfolio = await getPortfolioSymbolList(portfolio_name);
const jsonPortfolios = await data.json();
const symbols = [];
for await (let jsonPortfolio of jsonPortfolios) {
const price = await fetchPrice(jsonPortfolio.symbol);
symbols.push(price);
}
setSymbols(symbols);
}
fetchSymbols();
}, []);
return /** JSX **/
}

Howcome my state is not updating using react hooks and use Effect

My useEffect function is trying to fetch data from an API endpoint. The results resultAllTestTypes are currently logging fine.
However, I can't find out why the allTestTypes are coming back as undefined as I thought I had already set it in a state variable it should be able to log it to the console. But when I log the allTestTypes data it gives me this.
Code:
const [allTestTypes, setAllTestTypes] = useState([])
useEffect(() => {
async function onLoadCreateUnitTests() {
const results = await get('get_tables_autocomplete/b', user.user)
const resultsAllTestTypes = await get('get_all_test_types', user.user)
autoComplete.setTablesAutoComplete(results)
setAllTestTypes(resultsAllTestTypes)
console.log('resultAllTestTypes data ',resultsAllTestTypes.data);
console.log('allTestTypes data ',allTestTypes.data);
}
onLoadCreateUnitTests()
It's setting the state, you just have a console.log in a spot that's not particularly useful.
allTestTypes is a local const. It will never change, and that's not what setAllTestTypes is trying to do. When you set state, this tells react to render the component again. When that render occurs, you'll make a new call to useState, which will return the new value and assign it to a new local const. That new variable can be interacted with by code in the new render, but code from the previous render (such as your console.log) will never see the new value.
If you'd like to verify that the component is rerendering with a new value, move your console.log into the body of the component:
const [allTestTypes, setAllTestTypes] = useState([])
console.log('Rendering with', allTestTypes);
useEffect(() => {
async function onLoadCreateUnitTests() {
const results = await get('get_tables_autocomplete/b', user.user)
const resultsAllTestTypes = await get('get_all_test_types', user.user)
autoComplete.setTablesAutoComplete(results)
setAllTestTypes(resultsAllTestTypes)
}
onLoadCreateUnitTests()
});
cuz setAllTestTypes is async, so u can't get it immediately.
if u want to use it ,use the local variable resultsAllTestTypes instead

React: Stop hook from being called every re-rendering?

Somewhat new to React and hooks in React. I have a component that calls a communications hook inside of which a call to an API is made with AXIOS and then the JSON response is fed back to the component. The issue I'm having is the component is calling the hook like six times in a row, four of which of course come back with undefined data and then another two times which returns the expected JSON (the same both of those two times).
I did a quick console.log to double check if it was indeed the component calling the hook mulitple times or it was happening inside the hook, and it is the component.
How do I go about only have the hook called only once on demand and not multiple times like it is? Here's the part in question (not including the rest of the code in the widget because it doesn't pertain):
export default function TestWidget() {
//Fetch data from communicator
console.log("called");
const getJSONData = useCommunicatorAPI('https://jsonplaceholder.typicode.com/todos/1');
//Breakdown passed data
const {lastName, alertList, warningList} = getJSONData;
return (
<h1 id="welcomeTitle">Welcome {lastName}!</h1>
);
}
export const useCommunicatorAPI = (requestAPI, requestData) => {
const [{ data, loading, error }, refetch] = useAxios('https://jsonplaceholder.typicode.com/todos/1', []);
console.log("data in Communicator:", data);
return {data};
}
I would use the useEffect hook to do this on mount and whenever any dependencies of the request change (like if the url changed).
Here is what you will want to look at for useEffect
Here is what it might look like:
const [jsonData, setJsonData] = React.useState({})
const url = ...whatver the url is
React.useEffect(() => {
const doFetch = async () => {
const jsonData = await useAxios(url, []);;
setJsonData(jsonData)
}
doFetch();
}, [url])
...use jsonData from the useState
With the above example, the fetch will happen on mount and if the url changes.
Why not just use the hook directly?
export default function TestWidget() {
const [{ data, loading, error }, refetch] =
useAxios('https://jsonplaceholder.typicode.com/todos/1', []);
return (<h1 id="welcomeTitle">Welcome {lastName}!</h1>);
}
the empty array [] makes the hook fire once when called
Try creating a function with async/await where you fetch the data.
Here can you learn about it:
https://javascript.info/async-await

Resources