Call an API method via React Redux on Browser close using 'beforeunload' event listener - reactjs

I have a React App and I want there to be a DELETE call to an API method when a user closes the browser. I have therefore added this into the App.js:
import myReduxAction from "./Redux/Actions/admin/superAdminActions";
componentDidMount() {
...
this.setupBeforeUnloadListener();
}
setupBeforeUnloadListener = () => {
window.addEventListener("beforeunload", (ev) => {
this.methodToCallBeforeUnload();
debugger;
});
};
methodToCallBeforeUnload= () => {
myReduxAction.myActionMethodSynchronous();
}
When the browser is closed, I can enter a debugger into the above code and it seems to run through fine. However, I notice that the API method is not being hit. My Action method looks something like this:
const myActionMethodSynchronous = () => {
debugger;
return function () {
debugger;
try {
myApi.myAPIMethodSynchronous();
} catch (error) {
// Code to handle errors
}
};
};
And the actual call to the API:
const myAPIMethodSynchronous= () => {
debugger;
return axios.delete(`/MyAPI/myapimethodname/`);
};
I have added 2xdebugger; to the Action Method. One above the return function and another below. The first one gets hit, however the second one (and the one inside the api call) are never hit.
Am I missing something obvious here. I also have a button that is wired up to this Action Method and that seems to work fine. I only seem to have this problem when trying to initiate it from closing the browser.

What the browser/device do you use? onbeforeunload and onunload are not supported in all browsers. For example, this code:
window.onbeforeunload = function() {
fetch('/api/some_method1', {
method:'GET',
headers: {
'Content-Type': 'application/json'
},
})
}
window.onunload = function () {
fetch('/api/some_method2', {
method:'GET',
headers: {
'Content-Type': 'application/json'
},
})
}
works fully in Chrome and Opera (I got both requests), in Safari I got only onbeforeunload, and it seems that it is not working on iOS.

Related

FullCalendar events (as a json feed) is getting re-fetched after eventClick and dayClick

I am using FullCalendar in a React page and in order to get the events I am using events as a json feed (https://fullcalendar.io/docs/events-json-feed). The data gets loaded fine however when I use eventClick or dateClick to open a modal, FullCalendar is refreshing and another POST request is sent to the backend.
Is there a way to prevent that? I want to avoid sending unnecessary requests...
Also, as the data gets refreshed the calendar events are re-drawn and this causes to look like a glitch. Similar to this:
https://user-images.githubusercontent.com/3365507/85287154-6fc61c00-b4c6-11ea-83c1-cb72a3aec944.gif
Here are a few examples of the code I am using:
<FullCalendar
...
eventClick={handleEventClick}
dateClick={handleDateClick}
eventSources={[
{
events: fetchEvents,
failure: function() {
console.log('ERROR');
}
},
]}
...
/>
And fetchEvents is something like this:
const fetchEvents = (fetchInfo, successCallback, failureCallback) => {
fetch('http://localhost/calendarEvents', {
method: 'POST',
body: JSON.stringify(fetchInfo),
headers: {
'Content-Type': 'application/json',
},
})
.then((res) => res.json())
.then((data) => {
const parsedEvents = [];
for (const event of data) {
parsedEvents.push({
...event,
start: moment(event.startAt).toDate(),
end: moment(event.endAt).toDate(),
title: event.title
});
}
successCallback(parsedEvents);
})
.catch((error) => {
failureCallback(error);
});
}
and handleEventClick:
const handleEventClick = (event) => {
setSelectedEvent(event);
setOpenEventModal(true);
};
--EDIT--
Here is a CodeSandbox example:
https://codesandbox.io/s/suspicious-murdock-4jfept?file=/src/App.js
You can see at the Console tab that a new fetch is tried each time you click at a date to open the Modal. A new fetch is expected only when switching months in the calendar because I am using eventSources json feed option. But if it was already fetched it shouldn't do it again just by opening the Modal.
setSelectedEvent(event);
setOpenEventModal(true);
If state changes in <FullCalendar> it will rerender. This may be causing it to call for the data again.
Either stop changing the state in FullCalendar, do your API calls outside and pass in the data, or don't call for the data on every render.
What is the full code for <FullCalendar>?

How can I optimize my code to stop sending GET requests constantly?

I am using the Yelp Fusion API to get a list of restaurants from Yelp. However, I am always constantly sending a GET request and I am not sure what is going on or how to fix it. I have tried React.memo and useCallback. I think the problem lies within how I am making the call rather than my component rerendering.
Here is where I send a GET request
// Function for accessing Yelp Fusion API
const yelpFusionSearch = async () => {
try {
const response = await yelp.get('/businesses/search', {
params: {
term: food,
location: location
}
})
// Saving our results, getting first 5 restaurants,
// and turning off our loading screen
setYelpResults({businesses: response.data.businesses.splice(0, 5)});
setEnableLoading(1);
}
catch (error) {
setEnableLoading(2);
}
};
This is where I use axios.
// Our Yelp Fusion code that sends a GET request
export default axios.create({
baseURL: `${'https://cors-anywhere.herokuapp.com/'}https://api.yelp.com/v3`,
headers: {
Authorization: `Bearer ${KEY}`
},
})
You are probably calling that function within your functional component and that function sets a state of that component, so it re-renders. Then the function is executed again, sets state, re-renders and so on...
What you need to do is to wrap that API call inside a:
useEffect(() => {}, [])
Since you probably want to call it one time. See useEffect doc here
You can do 2 things either use a button to get the list of restaurants because you are firing your function again and again.
const yelpFusionSearch = async () => {
try {
const response = await yelp.get('/businesses/search', {
params: {
term: food,
location: location
}
})
Use a button instead maybe so once that button is clicked function is fired.
<button onClick={yelpFusionSearch} />Load More Restaurants </button>
Use your fuction inside useEffect method which will load 5 restaurants once the page renders
useEffect(() => {
const yelpFusionSearch = async () => {
try {
const response = await yelp.get('/businesses/search', {
params: {
term: food,
location: location
}
})
}, [])

Cypress stub function gets lost after loading my app

In my main React app's class componentDidMount I call an api method to fetch some data. I'm trying to test that my app does the right thing given the data. Rather than try and mock the server, and deal with Cypress's semi-support for fetch and whatnot, I'm trying to cy.stub the entire API function to just return a block of data.
// api.ts
export const fetchData = async (): Promise<IData> => {
...
}
// app.tsx
import { fetchData } from "../api";
export class App extends React.PureComponent<IProps, IState> {
async componentDidMount() {
const data = await fetchData();
// ...
}
}
// testData.test.ts
import * as Api from "../../src/api";
context("Test the app after loading mock data from the API", () => {
describe("Calling the API",() => {
before(() => {
cy.stub(Api, "fetchData", () => {
return Promise.resolve({
someData: "value"
});
});
cy.visit("/");
});
it("calls 'fetchData'", () => {
expect(Api.fetchData).to.be.called;
});
});
});
However, the app still calls the original version of fetchData instead of the stubbed version.
I tried experimenting by writing a test that simply calls a library method that itself imports fetchData, and that time the mock worked fine. So mocking an ES6 function that way should work. So it's something to do with loading my application that causes it to get lost.
This is not possible with cy.visit. You can use the new plugin #cypress/react to do the trick ;)
That's not really how the stubbing is supposed to work:
https://docs.cypress.io/guides/guides/network-requests.html#Stubbing
To begin stubbing responses you need to do two things.
Start a cy.server()
Provide a cy.route()
cy.server() // enable response stubbing
cy.route({
method: 'GET', // Route all GET requests
url: '/users/*', // that have a URL that matches '/users/*'
response: [] // and force the response to be: []
})
Not sure which API call you're trying to do, but this is a great headstart, and it doesn't require to have cypress any knowledge of your internal api work.
Not really knowing what you are trying to test, but a complete example would be this:
describe("Calling the API",() => {
cy.server() // enable response stubbing
cy.route({
method: 'GET', // Route all GET requests
url: '/users/*', // that have a URL that matches '/users/*'
response: [] // and force the response to be: []
})
.as('get-user')
.visit('/')
.wait('#get-user') // wait for your call to finish and assert it has been called
})

React Native Formik handleSubmit does not read return values from function

Good day!
Im having a weird experience using formik today,
I am currently working on an app that uses formik to handle forms and will do an API request inside the onSubmit() function of formik.
Every thing went well except when i use API request and wait for it's callback.
Somehow the things inside the function of onSubmit will work properly but the API callback value does not return unless i perform a UI Change in the app itself (like pressing random spots on my screen to trigger ui change).
Here is a look of my onSubmit function of formik
onSubmit={values => {
console.log("before")
let response = FunctionWithApiRequest(values);
console.log("after")
response.then((res) => {
console.log(res)
})
}}
and here is my function with api request inside
const FunctionWithApiRequest = (credentials) => {
return fetch(`${AppConfig.HOSTNAME}/v2/auth/signup`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(credentials)
})
.then((response) => response.json())
.then((responseJson) => {
return responseJson
})
.catch((error) => {
console.log(error)
});
}
The return "responseJson" will only appear inside the onsubmit function when i perform a UI Change (like clicking random spots in my react native screen)
i was wondering what is the problem and what cause the bug.
Thank you for your response in advance.
Possibly you can do this in a separate function with await and async.For instance
async handleSubmit(){
let {credentials} = this.state
let data = await this.props.FunctionWithApiRequest(credentials)
this.setState({returnedData: data})
}
And now in your textInput/formik
onSubmit={()=>this.handleSubmit()}
I assume you have made the request api function in actions.file, not in the same file.Am i right?So what i have done is just put await before call.Which means next line will execute only when you have response returned.Comment down if you have any issue.
It was caused by the haul bundler, when you enable dugging mode.

Data fetching with React Native + Redux not working

I am building my first React Native app and use Redux for the data flow inside my app.
I want to load some data from my Parse backend and display it on a ListView. My only issues at the moment is that for some reason, the request that I create using fetch() for some reason isn't actually fired. I went through the documentation and examples in the Redux docs and also read this really nice blog post. They essentially do what I am trying to achieve, but I don't know where my implementation differs from their code samples.
Here is what I have setup at the moment (shortened to show only relevant parts):
OverviewRootComponent.js
class OverviewRootComponent extends Component {
componentDidMount() {
const { dispatch } = this.props
dispatch( fetchOrganizations() )
}
}
Actions.js
export const fetchOrganizations = () => {
console.log('Actions - fetchOrganizations');
return (dispatch) => {
console.log('Actions - return promise');
return
fetch('https://api.parse.com/1/classes/Organization', {
method: 'GET',
headers: {
'X-Parse-Application-Id': 'xxx',
'X-Parse-REST-API-Key': 'xxx',
}
})
.then( (response) => {
console.log('fetchOrganizations - did receive response: ', response)
response.text()
})
.then( (responseText) => {
console.log('fetchOrganizations - received response, now dispatch: ', responseText);
dispatch( receiveOrganizations(responseText) )
})
.catch( (error) => {
console.warn(error)
})
}
}
When I am calling dispatch( fetchOrganizations() ) like this, I do see the logs until Actions - return promise, but it doesn't seem to actually to fire off the request. I'm not really sure how how I can further debug this or what resources to consult that help me solve this issue.
I'm assuming that Redux is expecting a Promise rather than a function.. Is that true?
If so, I think your return function may not be working.
You have a new line after your return, and it's possible JavaScript is (helpfully) inserting a semicolon there.
See here: Why doesn't a Javascript return statement work when the return value is on a new line?

Resources