React - useEffect - javascript

So i have a simple to-do app, click here to [Link removed].
The problem i have is that in Console > Network i get too many same GET requests so it looks like
i have an infinite loop. Im probably not coding this right in useEffect, since the effect triggers my http get methods which contain setTodos(data) which makes it trigger useEffect again.
What im trying to achieve is the same functionality that the web app has right in this moment, but with appropriate coding:
Fetching the appropriate data every time user clicks some category.
I also want to trigger fetch for specific category each time user either adds/edits/deletes a 'todo' so i can achieve 'live feed'.
EDIT : What i tried right now is implementing useState [test,setTest]. I added setTest(!test) to my funcs: addTodo, removeTodo, markTodo and to my category btn onClick func. I also put [test] to dep. array in my useEffect. Sometimes list updates correctly, sometimes it doesnt at all, and sometimes it udpates but incorrectly (ex: when i mark a todo, it gets marked/crossed and then it gets unmarked 1sec after. But if i refresh the page its ok).
useEffect
useEffect(() => {
if(filter === 'Completed')
httpRequestHandler(`${baseAPIurl}/completed`, "GET");
else if(filter === 'Active')
httpRequestHandler(`${baseAPIurl}/notcompleted`, "GET");
else
httpRequestHandler(`${baseAPIurl}/todos`, "GET");
});
And here is part of my httpRequestHandler function
const httpRequestHandler = async (url, type, data) =>
{
let request = {}
if(type === 'GET')
{
request = {
method: "GET",
headers: {"Content-type": "application/json"}
};
await fetch(url, request)
.then((response)=> {
return response.json();
})
.then((data)=> {
setTodos(data);
});
return;
}
...........................
}

Depending on the rest of your code, you need to adjust the dependency array
more info, eg. here

Have you tried adding filter into dependency array to only update on filter change?
useEffect(() => { ... }, [filter])

No idea what was causing my useEffect not to trigger in the first place, i already tried this but now it works (no idea why). What i did is:
Implemented useState [trigger, setTrigger]
In my httpHandler func (which handles http requests), specifically in PUT/DELETE/POST part of the func where i use fetch(--, --) i added setTrigger(!trigger) to .then() part.
Added [trigger] to useEffect dependency array

Related

How to properly Populate a Dropdown with SQL Query Values in React.Js Hook

I have a react component called Sidebar.jsx. Within it, I am making an API call to get a array of fleets to populate an eventual JSX dropdown element within my Sidebar. This results in a simple JSON array.
I have imported a function called getFleets() from my services folder to make the API call. The service uses the fetch API to make a query call to my backend and looks like this:
export async function getFleets() {
const resp = await fetch("http://localhost:5000/fleets", {
method: 'GET',
headers: {},
mode: 'cors'
});
return resp.json();
};
However, when I use the website, it appears to infinitely make the API call. This is my first time trying to make an API call within a react component so I am a bit confused here. Other guides I've read online seem to be similar but I am obviously missing something.
What can I do to make this API call only once and retrieve my JSON array such that I can later use it to populate the options in my return ?
Sidebar.jsx
import React, { useEffect, useState } from "react";
import { getFleets } from "../services/FleetService";
const Sidebar = () => {
const [data, setData] = useState([]);
useEffect(() => {
const setFleets = async () => {
const fleets = await getFleets();
console.log(fleets);
setData(fleets);
}
setFleets();
}, [data]);
return (
<>
// Add data to <select> </select>
);
};
The way your code works, since data is part of the dependency array sent to useEffect, every time data changes the effect runs, which changes data, which runs the effect again ...resulting in the infinite loop.
The simple fix is to remove data from the dependency array, and explicitly specifying an empty array [] as the second parameter of useEffect. This will make the effect run only exactly once, when the component is first rendered.
You need to explicitly specify an empty array because when the second parameter isn't specified at all, the effect will run on every render, bringing back the infinite loop issue.

How can I update a state variable from a promise?

I am trying to determine if a customer has an active subscription or not. To do this I am utilizing the following code:
const stripe = require('stripe')('some-api-key');
export default function Example(){
// the user will automatically be considered non-subbed by default
const [isSubscriber, setIsSubscriber] = useState(false)
// grab the customer id from stripe
async function get_customer_id() {
const customers = await stripe.customers.search({
query: `metadata[\'some-meta-data-key\']:\'some-meta-data-value\'`
});
return customers.data[0]['id']
}
// grab the list of active subscriptions from stripe
async function customer_is_subscriber(){
const subs = await stripe.subscriptions.list({
status: 'active',
});
return subs
}
// determine if the customer id is in the list of active subscriptions.
// return true if so, false otherwise
async function test_equality(){
const customer_id = await get_customer_id();
const subbed = await customer_is_subscriber();
const answer = subbed.find(sub => sub.customer === customer_id)
return !!answer;
}
useEffect( () => {
async function load_result() {
const promise_function_return = await test_equality()
setIsSubscriber(promise_function_return)
}
load_result();
}, [isSubscriber]);
return (
// some react code
)
}
I have been able to successfully get all of my other functions where I am doing the comparisons for if a user is a subscriber but where I am having an issue is updating the state value (e.g. true if they are subbed, false otherwise).
I found some good past questions on this specific topic such as:
here The useState set method is not reflecting a change immediately
here: setState inside Promise in React
and here: setState inside a Promise function in a useEffect with hooks?
but I just have not been able to get it working correctly. This is currently the closest I have been able to get to solving this problem.
Currently your code says that, when isSubscriber changes, it should check if the user is a subscriber and update the isSubscriber state... so it's a chicken and egg problem. It won't set isSubscriber until isSubscriber gets set.
I think you want to change }, [isSubscriber]); to }, []); so that that code executes when the component first loads (not when isSubscriber changes).
The useEffect hook will always run on mount regardless of if there is anything in its dependency array. This means that your useEffect will work as is, and will run onMount as well as when isSubscriber changes:
useEffect( () => {
async function load_result() {
const promise_function_return = await test_equality()
setIsSubscriber(promise_function_return)
}
load_result();
}, [isSubscriber]);
To verify this, you can check out this codesandbox example. The useEffect looks just like yours, and you will notice that isSubscriber is initially set to false, but is updated to true after 3 seconds.
There's still an adjustment you may want to make even though that part appears to work ok. With isSubscriber in the dependency array, the function in your useEffect will be called any time isSubscriber changes. This probably not what you want, since this function doesn't actually depend on isSubscriber, but actually sets isSubscriber. In this case, that means test_equality() will be run on initial mount and then one more time after it sets isSubscriber, which is unnecessary.
This blog post explains the useEffect dependency array really well.
You can fix this by removing isSubscriber from the dependency array, like this:
useEffect(() => {
console.log("in useEffect");
async function load_result() {
const promise_function_return = await test_equality();
setIsSubscriber(promise_function_return);
}
load_result();
}, [isSubscriber]);
Since you mentioned the state value is not getting updated, there must be another issue going on in either get_customer_id() or customer_is_subscriber(). It would be good to double check and make sure the stripe api calls are working as expected.

Why is my RXJS epic in an infinite loop on mount but only called once on button click?

please take a look at this code below
basically what is happening my action is being dispatched here:
useEffect(() => {
fetchData()
setLoaded(true)
}, [])
but for some reason this is infinite looping and causing my action to be dispatched continuously
export const fetchData = () => ({ type: 'GET_USER_DATA' })
and this is triggering my epic
const getUserData = (action$, state$) =>
action$.pipe(
ofType('GET_USER_DATA'),
mergeMap(
(action) =>
ajax
.getJSON(
`myurlishere`,
)
.pipe(map((response) => fetchUserFulfilled(response))),
)
)
which trigger this:
const fetchUserFulfilled = (payload) => ({ type: 'GET_DATA_SUCCESS', data: payload })
this code all works but it's continuously calling it in an infinite loop
however, if I move the code from useEffect to a button call like so:
<button onClick={fetchData}>fetch</button>
it only calls it once, which is what I want
but I need the data to be called onmount. so how do I fix it?
please note I have tried adding various things to the second argument of useEffect but it's having no effect
useEffect(() => {
fetchData()
setLoaded(true)
}, [user.id])
Based solely off the provided code, I don't see any issues. My gut suggest a few options, even though you mentioned some of them I would triple check:
Missing dependencies array as second arg to useEffect, or you're using a variable for it but the variable has an undefined value which would have the same problem.
If you are using useEffect dependencies, perhaps one of them is constantly changing unknowingly. e.g. objects often change in identity between renders, {} !== {}
There is code not shown that is also dispatching the same action, and in fact that useEffect is only running once.
Some parent is rendering one of the ancestors or this component with a key={something} and the value provided changes on each render. If that happens, the component is torn down and recreated every time from scratch.
If you are 100% positive you are providing useEffect(work, []), an empty array as second argument, but the effect is in fact confirmed to be running in an infinite loop, synchronously, then the forth possibility is likely.
If you typed these code examples in the question by hand when posting this, do not trust that you implemented them the same way as what you think your app is doing. Triple check. Ideally have someone else check who didn't write the code. Often the problem is what we think we've told our code to do is not what we've actually told it to do. If you haven't already, your best bet is to step through the code with a debugger so you can see what's happening.
Hope this helps!
this is the code now working:
export const getUserDataEpic = () => {
return ajax
.getJSON(myurl)
.pipe(
map((response) => fetchUserFulfilled(response)),
)
}
I know I'm not listening to the action fired now, but why would that be causing an infinite loop?

React Router -- history push state not refreshing with new state object

When Promise.all resolves and the new activity is saved, the user should be routed to /activities to view their newly created activity. Everything works as expected, however I currently need to refresh /activities page (once) after being routed in order to view the new activity in the table.
const handleSaveActivity = e => {
e.preventDefault();
Promise.all([
addActivity(),
saveActivity()
]).then(() => {
props.history.push('/activities');
})
};
I'm not sure how to re-render the page automatically after pushing a new history state, so the user does not need to manually refresh the page to see the new state. Happy to provide more code snippets if I left out something critical.
Hi i must be a little late to answer this, but this issue can be due to the wrong use of useEffect, if you have lets say a todo list and you wanna fetch data with axios for example, it would look like this:
useEffect(()=>{
axios.get(`${YOUR_URL}/todos`)
.then((res)=>{
setTodos(todos=res.data)
})
},[])
now as you can see we have initial value of an empty array, so this is acting as a ComponentDidMount, what you might want is to re render the component after it gets a new value, so you want to have a ComponentDidUpdate effect, so you would just not initialize the value as an empty array, therefore it would look like this:
useEffect(()=>{
axios.get(`${YOUR_URL}/todos`)
.then((res)=>{
setTodos(todos=res.data)
})
})
Hope this helps someone, couse i landed here due to the same issue and came to solve it this way.
just to run this.setState({whateverKey:whateverValue})?
In your activities page (call it Activities component) you should call API to get the updated data every time browser hit this component URL.
With class based style, you should do it in componentDidMount life cycle hook
class Activities extends Component {
// ...
componentDidMount() { loadActivities() }
// ...
}
With function based style, you should do it in useEffect hook
import React, { useEffect } from 'react'
const Activities = () => {
useEffect(() => { loadActivities() });
}
https://github.com/supasate/connected-react-router Please use this package, it solves the problem.
This issue I've faced a few minutes ago...however I finally found the solution by manually using the vanilla javascript. => for refreshing the page you can use
=> window.location.reload(false); after using the push property.

Page not refreshed when clicked delete button in angular

I have a page running at "http://localhost:4200/assignmentForAudit" and the UI looks like
When i click delete button then the data gets deleted but the page is not refreshed. I tried to put this.router.navigate(['/assignmentForAudit']); after delete operation but it is not refreshing the page.How can i achieve this refresh method so that the data gets removed?
method to delete in component.ts
onDelete(nas:any){
this.assignmentAudit.id=nas.assignmentAudit[0].id;
console.log(this.assignmentAudit.id);
if(window.confirm('Are sure you want to delete this item ?')){
this.assignmentAuditService.deleteGroupFromSelection(this.assignmentAudit.id)
.subscribe(
data => {
console.log(data);
},
error => console.log(error));
}
this.router.navigate(['/assignmentForAudit']);
}
assignment-audit.service.ts class method to call the api operation
deleteGroupFromSelection(id: number): Observable<any> {
return this.http.delete(`${this.baseUrl}/${id}`, { responseType: 'text' });
}
Delete operation is working fine but the problem is the page is not refreshing.
that's not recommended at all as a best practice when you using angular you can simple emit data list after every delete with that you update the data visually too but if you want such behavior.
First Case by reload the whole page you can do so after every delete
window.location.reload();
Second Case if you need just to reload the component you can work around that and achieve it by a hack (just to trick the component you navigate away and navigate again to it)
this.router.navigateByUrl('/DummyComponent', {skipLocationChange: true}).then(() => this.router.navigate(['Your actualComponent you want to reload']));
/DummyComponent could be a empty component you just gonna use it to trick the actual component you need to refresh/reload
Instead of trying to reload the page, call the http method which is used to populate the entries again on success of the delete http call.
this.assignmentAuditService.deleteGroupFromSelection(this.assignmentAudit.id)
.subscribe(
data => {
this.entries = this.http.get() // something like this. here this.entries refers to data that is used to populate html.
console.log(data);
},
error => console.log(error));
}
You can also use a BehaviorSubject to emit a value, listening to it, you can decide on calling this.entries = this.http.get() again.
You may need to use the 'onSameUrlNavigation' option in Angular routes.
RouterModule.forRoot(routes, {onSameUrlNavigation: ‘reload’})
Can your show how to binding array of object to template ? I thinks you just remove item from array then template it will update data too.
something like this:
this.nas.assignmentAudit = this.nas.assignmentAudit.filter(a => a.id !== this.assignmentAudit.id);

Categories

Resources