This question already has answers here:
Why useEffect running twice and how to handle it well in React?
(2 answers)
Closed 7 months ago.
I am making a simple get request in React like the following:
function App() {
const [req, setReq] = useState();
const getBasic = () => {
axios
.get(
"https://financialmodelingprep.com/api/v4/price-target?symbol=AAPL&apikey=********"
)
.then((result) => setReq(result));
};
useEffect(() => {
getBasic();
}, []);
console.log(req);
return (
<div>
</div>
);
}
The above code logs the same thing four times in the console.
Was not sure if moving the console.log(req) into the return would do anything different, like:
return (
<div>
{console.log(req)}
</div>
);
It did not however and had the same result.
I tried to do the same code but with async await syntax:
function App() {
const [req, setReq] = useState();
const getBasic = async () => {
const getReq = await axios.get(
"https://financialmodelingprep.com/api/v4/price-target?symbol=AAPL&apikey=**********"
);
setReq(getReq);
};
useEffect(() => {
getBasic();
}, []);
console.log(req);
return (
<div>
</div>
);
}
Still the same, getting four seperate console.log(req)'s in console.
The last thing I tried was using a onClick button instead of useEffect to make a request
return(
<div>
<button onClick={getBasic} />
</div>
)
this results into two logs per click instead of four with useEffect.
I do not understand if in these instances I am making two or four requests or if I am still only making one request but it is logging multiple times, I would like to know why this is happening and what I should do to fix it, even though I can do the rest of my project with it working this way it appears I am not doing it optimally either way.
Disable Strict Mode by deleting Strict Mode tags in index.js
Related
I've got something I don't understand here. I'm creating a hierarchy of React functional components like so:
const ContainerComponent = () => {
const [total, setTotal] = useState(initialValue);
const updateFunction = () => { console.log('It got called'); }
return (
<EntryComponent updateFunction={updateFunction} />
);
}
const EntryComponent = ({updateFunction}) => {
const services = [{}, {}, {}];
return (
{services.map((service) => <ServiceComponent updateFunction={updateFunction} />}
);
}
const ServiceComponent = ({updateFunction}) => {
return (
<input type='checkbox' onChange={updateFunction} ></input>
);
}
So the idea is that the function gets passed all the way to the ServiceComponent and attached to the checkbox for each component. On change the function should fire, running the code and updating the total value in the ContainerComponent. Problem is that this isn't happening properly and the function seems only to be passed to the first ServiceComponent instance. If I drop a console.log(updateFunction) into ServiceComponent I see the function logged for the first component, and undefined for the remaining two. This is strange even for JavaScript. Can anyone shed any light as to what is going on? As far as I understand the function should be able to be passed like any other variable, and each ServiceComponent should have it to use when needed. Indeed, if I pass a second property to the child, an object or a primitive like an integer it comes through just fine on every child component. Why is this not occurring for my function, and how do I fix it?
Edit: I see the problem, and the problem is I'm not as smart as I think I am. In the condensed version I put here everything is being supplied to the child components in the same loop, but in the actual project the child components are created in multiple places and I neglected to pass the function property to all of them. Which is mind mindbogglingly stupid on my part. I'm leaving the question here as many of you have posted replies, for which I'm grateful.
Programming is hard, I think I need a better brain.
I tried refactoring your code to this
const ContainerComponent = () => {
const [total, setTotal] = useState(0);
const updateFunction = (e) => {
console.log("update total stuff happens here");
console.log(e);
};
return <EntryComponent updateFunction={updateFunction} />;
};
const EntryComponent = ({ updateFunction }) => {
const services = ["one", "two", "three"];
return (
<>
{services.map((service) => (
<ServiceComponent updateFunction={updateFunction} />
))}
</>
);
};
const ServiceComponent = ({ updateFunction }) => (
<input type="checkbox" onChange={updateFunction}></input>
);
and it works just fine.
Try also using a react fragment in your entry component.
This question already has answers here:
Why useEffect running twice and how to handle it well in React?
(2 answers)
Closed 9 months ago.
I have the following code snippet in use while wrapping my whole React application with <React.StrictMode>.
function Quiz() {
const fetchData = useCallback(
async () => {
console.log("hiho");
},
[]
);
useEffect(() => {
fetchData();
}, [fetchData])
return (
<>
</>
)
}
For the initial load of my application fetchData is being called twice within my useEffect(). I am a bit puzzled as I assumed that useCallback() would prevent this (even though StrictMode calls the rendering twice).
Should I be worried that fetchData get's called twice? In my case fetchData returns random data which then has the side effect that the data changes during the render process on dev.
Try removing the fetchData from the useEffect dependency array. I believe that should render it just once.
function Quiz() {
const fetchData = useCallback(
async () => {
console.log("hiho");
},
[]
);
useEffect(() => {
fetchData();
}, [])
return (
<>
</>
)
}
I'm having an issue when trying to save to State an axios API call. I've tried
useState set method not reflecting change immediately 's answer and many other and I can't get the state saved. This is not a duplicate, because I've tried what the accepted answer is and the one below and it still doesn't work.
Here's the (rather simple) component. Any help will be appreciated
export const Home = () => {
const [widgets, setWidgets] = useState([]);
useEffect(() => {
axios
.get('/call-to-api')
.then((response) => {
const data = response.data;
console.log(data); // returns correctly filled array
setWidgets(widgets, data);
console.log(widgets); // returns '[]'
});
}, []); // If I set 'widgets' here, my endpoint gets spammed
return (
<Fragment>
{/* {widgets.map((widget) => { // commented because it fails
<div>{widget.name}</div>;
})} */}
</Fragment>
);
};
Welcome to stackoverflow, first thing first the setting call is incorrect you must use spread operator to combine to array into one so change it to setWidgets([...widgets, ...data]); would be correct (I assume both widgets and data are Array)
second, react state won't change synchronously
.then((response) => {
const data = response.data;
console.log(data); // returns correctly filled array
setWidgets(widgets, data);
console.log(widgets); // <--- this will output the old state since the setWidgets above won't do it's work till the next re-render
so in order to listen to the state change you must use useEffect hook
useEffect(() => {
console.log("Changed Widgets: ", widgets)
}, [widgets])
this will console log anytime widget changes
the complete code will look like this
export const Home = () => {
const [widgets, setWidgets] = useState([]);
useEffect(() => {
axios
.get('/call-to-api')
.then((response) => {
const data = response.data;
setWidgets([...widgets, ...data])
});
}, []);
useEffect(() => {
console.log("Changed Widgets: ", widgets)
}, [widgets])
return (
<Fragment>
{/* {widgets.map((widget) => { // commented because it fails
<div>{widget.name}</div>;
})} */}
</Fragment>
);
};
Try:
setWidgets(data);
istead of
setWidgets(widgets, data);
Your widgets.map() probably fails because there isn't much to map over when the component is being rendered.
You should update it with a conditional like so, just for clarity:
widgets.length>0 ? widgets.map(...) : <div>No results</div>
And your call to setWidgets() should only take one argument, the data:
setWidgets(data)
or if you want to merge the arrays use a spread operator (but then you need to add widgets as the dependency to the useEffect dependency array.
setWidgets(...widgets, ...data)
You might also have to supply the setWidgets hook function to the useEffect dependency array.
Let me know if this helps..
I've been trying to look for an answer everywhere but this is really driving me crazy.
Been using StackOverflow for a while, yet this became my first question.
I'm trying to build a dynamic table using react.
For this, I am passing the state using the useState hook to an external component -which actually builds the table- through its props.
The reason not to build it on the same component is that I'm re-using the external component.
So far, this is the code where the hooks are being used
Commands.js
export default function Commands() {
const [data, setData] = useState([])
const fetchData = () => {
axios.get(`${server}${endpoint}`).then(ret => {
setData(ret.data)
}).catch(e => {console.log(e)})
}
useEffect(() => {
fetchData()
}, [])
return (
<Container>
<CommandsTable data={data}/>
</Container>
)
}
And this is the code where the data is used, although I think it should be unrelated.
CommandsTable.js
// ...
<tbody>
{
props.data.map(command => {
<tr key={command._id}>
<td>{command.name}</td>
<td>{command.ret}</td>
// etc
</tr>
})
}
</tbody>
//...
The problem: data always comes out not as undefined, but as empty (I assume it's the [] initial value I assign with useState)
One more thing I find funny is that even if I'm using axios-debug-log, it never logs any call, but using it anywhere else on the code, the same call works without issue.
What am I missing?
Thanks!
I'm assuming that the ret object is coming back as something you're not expecting.
I would suggest taking a peak at the ret variable when it arrives.
export default function Commands() {
const [data, setData] = useState([])
const fetchData = () => {
axios.get(`${server}${endpoint}`).then(ret => {
console.log({ ret })
setData(ret.data)
}).catch(e => {console.log(e)})
}
useEffect(() => {
fetchData()
}, [])
return (
<Container>
<CommandsTable data={data}/>
</Container>
)
}
Figured it out thanks to #Ajay 's comment.
It was a CORS policy issue. This is what I've done to solve it.
Server side (express server)
Install cors
npm i cors
Require and use it
const cors = require("cors")
app.use(cors())
Restart server, solved.
Thanks guys!
edit: changed pretty much the whole body of this question
I am building a booking app. Tons of rerenders are happening and, following #jpmarks advice, I have tried to find out which useEffect could be the culprit, and think might be the piece of code below, which runs 24 times.
I have also uploaded all of the relevant components and console warnings to this repo, assuming that other components which render this may be affecting it.
useEffect(
() => {
console.log('useEffect ServicEstabelecimento ran') //runs 24 times
async function getOfferedServicesFromDB() {
const approved = await approvedBusinessService.doc(businessId)
const snapshotApproved = await approved.get()
if (snapshotApproved.exists && snapshotApproved.data().offeredServices) {
setOfferedServices(Object.values(snapshotApproved.data().offeredServices))
} else {
const pending = await businessPendingApprovalService.doc(businessId)
const snapshotPending = await pending.get()
if (snapshotPending.exists && snapshotPending.data().offeredServices)
setOfferedServices(Object.values(snapshotPending.data().offeredServices))
}
return
}
getOfferedServicesFromDB()
},
[
/* businessId, setOfferedServices */
],
)
//React Hook useEffect has missing dependencies:
//'businessId' and 'setOfferedServices'.
//Either include them or remove the dependency array
//Tried adding them separately or both, nothing changes
What I am trying to do here is see which services a business offers, getting that info from the database. If the business has been accepted or not yet, there's a change in how that's dealt with.
I looked through your source code and it looks like the component is not responsible for the unneccessary renders. I recommend fixing the dependencies as your editor is telling you, see if that is fixing something.
I strongly believe its your context that gets updated inside of one of your effects that causes the context to rerender the component.
To find out what state from context is causing the renders you can use the useEffect on each of them to check.
const { theme, userObject, dateForRenderingTimeGrid } = useContext(Context);
useEffect(() => {
console.log("Theme updated");
}, [theme]);
useEffect(() => {
console.log("UserObject updated");
}, [userObject]);
useEffect(() => {
console.log("DateForRenderingTimeGrid updated");
}, [dateForRenderingTimeGrid ]);
Let me know what you are seing. If none of these are actually triggering an effect but you still see the renders, then you can be certain that its happening in your component
One possibility is that in Options.js you are rendering ServiceEstabelecimento multiple times using serviceList.map. The serviceList is set in the useEffect with serviceListService.collection().onSnapshot callback. With this, naturally your useEffect will be called 24 times(if serviceList gets a value of 24 in snapshot callback)
Another possibility is that you are rendering ServiceEstabelecimento with a ternary. This will unmount/re-mount the componentthe component based on the value of estabelecimento.
const ContextOptionsEstabelecimento = React.createContext()
export const Options = () => {
const { estabelecimento, theme, offeredServices } = useContext(Context)
const [isConfiguring, setIsConfiguring] = useState(false)
const [serviceList, setServiceList] = useState([])
useEffect(() => {
const unsubscribeServices = serviceListService.collection().onSnapshot(snapshot => {
const services = snapshot.docs.map(collectIdsAndDocs)
setServiceList(services) //<-----see here
})
return () => {
unsubscribeServices()
}
}, [])
const serviceElements = serviceList.map(service => //<-----see here
!estabelecimento ? ( //<-----see here
<Service
key={service.id}
id={service.id}
name={service.name}
type={service.type}
title={service.info}
duration={service.duration}
/>
) : ( //<-----see here
<ContextOptionsEstabelecimento.Provider value={{ isConfiguring }} key={service.id}>
<ServiceEstabelecimento
key={service.id}
id={service.id}
name={service.name}
type={service.type}
title={service.info}
duration={service.duration}
/>
</ContextOptionsEstabelecimento.Provider>
),
)
return (
...
Make checks on the above and see how you go. Also share the complete repo(if possible) to debug further.
Also I see some architecting issues as I see large objects been used with multiple contexts. This will have risk of components re-rendering unnecessarily. I am assuming you are taking care of such kind of things.