Is there a way in React JS to block a request if it is already doing it, i am talking of the same request.
EDIT:
This is my code:
const fetching = false;
export default (type, filter, dateFilter, position) => {
if(fetching) return Promise.reject(new Error('Request in progress'));
fetching = true;
return fetch(URL + `/search/${type}/${filter}/${dateFilter}/${position}/0/0`)
.then(response => Promise.all([response, response.json()]))
//!!! My problem is that now i cannot put .then(() => fetching = false))
// here.If i put it i get undefined in my then(res) of my getDataApi
// so i cannot make requests again because fetching stays in true.
}
for better understanding this is my console with:
.then(() => {
fetching = false;
console.log("fetching", fetching)
})
and without:
actions.js
export const fetchData = (type, filter, dateFilter, position) => {
return (dispatch, getState) => {
const state = getState();
dispatch(getData())
getDataApi(type, filter, dateFilter, position)
.then(res => {
console.log("RES", res)
if (res !== undefined) {
console.log("entro")
//here it doesnt enter if i put fething false above
// is like somehow the promise.all is not resolved if i
// put it above or under the then with the fetching =
// false but i need it, what can i do?
if (state.dataReducer.data.length === 0) {
dispatch(getDataSuccess(res[1]))
} else {
dispatch(getDataSuccess(res[1], state.dataReducer.data))
}
}
})
.catch((err) => console.log(9999, err))
}
}
Not sure you really need to over complicate this, hold some state that indicates your request is already in progress so subsequent requests can be ignored.
You don't mention how you are managing your app state so here's a very simple example based on your code
let fetching = false;
export default (type, filter, dateFilter, position) => {
if (fetching) return Promise.resolve();
fetching = true;
return fetch('...')
.then(response => {
// handle response
})
.catch(e => {
// handle error
})
.then(() => {
fetching = false; // reset state
});
}
I solved it.This is my code:
return fetch(URL + `/search/${type}/${filter}/${dateFilter}/${position}/0/0`)
.then(response => Promise.all([response, response.json()]))
.then(([response, responseObj]) => {
fetching = false;
return [response, responseObj];
})
.catch(err => {
fetching = false;
return Promise.reject(err); // If you want to handle the error in a chained .catch()
})
Related
I have a react class.
componentDidMount() {
let refresh_token = localStorage.getItem('refresh_token');
let isTokenActive = this.checkIfRefreshTokenWorking(refresh_token);
}
checkIfRefreshTokenWorking = (refresh_token) => {
let trueOrFalse = false;
fetch('http://localhost:8888/refresh_token?refresh_token='+refresh_token)
.then(response => {
response.json()
trueOrFalse = true;
})
.catch(err => {
trueOrFalse = false;
})
.finally(() => {
return trueOrFalse;
})
}
isTokenActive gets undefined. I want to wait until checkIfRefreshTokenWorking returns a value. Do I need to create a promise for this?
Javascript Promises
The function passed to new Promise is called the executor. When new Promise is created, the executor runs automatically. It contains the producing code which should eventually produce the result. In terms of the analogy above: the executor is the โsingerโ.
Its arguments resolve and reject are callbacks provided by JavaScript itself. Our code is only inside the executor.
This is not tested code
componentDidMount() {
let refresh_token = localStorage.getItem('access_token');
this.checkIfRefreshTokenWorking(refresh_token)
.then((data) => console.log(data))
.catch(error => console.log(error));
}
checkIfRefreshTokenWorking = (refresh_token) => new Promise((resolve, reject) => {
let trueOrFalse = false;
fetch('http://localhost:8888/refresh_token?refresh_token='+refresh_token)
.then(response => {
response.json()
trueOrFalse = true;
resolve(response.json());
})
.catch(err => {
trueOrFalse = false;
reject(err);
})
.finally(() => {
return trueOrFalse;
resolve();
})
})
You have two options here
Using async/await with refresh token call
Make use of conditional rendering way
// You need to use babel present env for this
async componentDidMount() {
const refresh_token = localStorage.getItem('access_token');
let isTokenActive = false;
try {
const response = awit fetch('http://localhost:8888/refresh_token?refresh_token=' + refresh_token);
const data = await response.json();
// console.log(data);
isTokenActive = true;
} catch(err) {
// Log the error
}
}
// Conditional rendering
this.state = {
...,
isLoading: false,
isTokenActive: false
};
componentDidMount() {
const refresh_token = localStorage.getItem('access_token');
this.setState({ isLoading: true });
fetch('http://localhost:8888/refresh_token?refresh_token=' + refresh_token)
.then(...)
.catch(...)
.finally(...); // Set the value for isLoading/isTokenActive with this.setState accordingly
}
render() {
const { isLoading, isTokenActive } = this.state;
!isLoading ? isTokenActive ? <MyComponent /> : <div /> : <div>Loading...</div>;
}
References:
https://www.valentinog.com/blog/await-react
https://reactjs.org/docs/conditional-rendering.html
so I'm using a popup to log my users in with firebase:
const loginToApp = (provider) => {
firebaseApp
.auth()
.signInWithPopup(provider)
.then(async (result) => {
if (result.additionalUserInfo.isNewUser) {
// problem is this line
await setNewUserInformation(result.user.uid)
}
const { user } = result
setUser(user)
// and this line
window.location.href = 'newRoute'
})
.catch((error) => {
console.log('ERROR:', error)
})
}
so if I remove window.location.href = 'visited' this all works fine and it sets in firebase. I'm probably doing something stupid but I cant figure out how to wait for this function to fire setNewUserInformation and to complete before I move to the new page?
function code:
export const setNewUserInformation = (userId) => {
return {
type: 'SET_NEW_USER_INFORMATION',
userId,
}
}
this then has a redux observable epic listening to it:
return action$.pipe(
ofType('SET_NEW_USER_INFORMATION'),
mergeMap((action) => {
return from(
firebaseApp.database().ref(firebaseRef).update(userInformation),
).pipe(
mergeMap(() => {
return [updatedUserInformationSuccess()]
}),
catchError((error) => of(updatedUserInformationFailure(error))),
)
}),
)
setNewUserInformation() is an action creator, which is sync. You do not need to wait for it as it does not return anything useful to you logic. What you need to do, is move window.location.href = 'newRoute' to separate logic, and make it depend on state returned from action creators updatedUserInformationSuccess() and updatedUserInformationFailure(error). If your component is functional, put this logic in a useEffect. If it is a class component, use ComponentDidUpdate lifecycle method.
Use it like below
const loginToApp = (provider) => {
firebaseApp
.auth()
.signInWithPopup(provider)
.then(async (result) => {
new Promise((resolve, reject) => {
if (result.additionalUserInfo.isNewUser) {
// problem is this line
setNewUserInformation(result.user.uid)
}
const { user } = result
resolve(user)
}).then((user)=>{
setUser(user)
// and this line
window.location.href = 'newRoute'
})
})
.catch((error) => {
console.log('ERROR:', error)
})
}
Because on then You can returned a Promise and resolve later. We could re-write the code above like this below:
const loginToApp = (provider) => {
firebaseApp
.auth()
.signInWithPopup(provider)
.then((result) => {
if (result.additionalUserInfo.isNewUser) {
// return for next resolve function
return setNewUserInformation(result.user.uid).then(() => result);
}
return result;
})
.then((result) => {
// after all above promises resolve
const { user } = result
setUser(user)
// and this line
window.location.href = 'newRoute'
})
.catch((error) => {
console.log('ERROR:', error)
})
}
Are you using React?
If yes, then you can simply use didUpdate Cycle to route to new url after successful action dispatched. Move your "window.location.href = 'newRoute'" under the ComponentDidUpdate with props check.
All code work good, but when I put then to the returned module all code crashes and throws error. Is the problem from that the export is function? If it is not from the function may someone explain why?
This is the module
export default {
search: function(searchTerm, searchLimit, sortBy) {
fetch(
`http://www.reddit.com/search.json?q=${searchTerm}&sort=${sortBy}&limit=${searchLimit}`
)
.then(res => res.json())
.then(data => data.data.children.map(data => data.data))
.catch(err => console.log(err));
}
};
This is actual main JavaScript file
import reddit from "./redditApi";
const searchForm = document.querySelector("#search-form");
const searchInput = document.querySelector("#search-input");
// form eventlistener
searchForm.addEventListener("submit", e => {
e.preventDefault();
// get search term
const searchTerm = searchInput.value;
// get sort
const sortBy = document.querySelector('input[name="sortby"]:checked').value;
// get limit
const searchLimit = document.querySelector("#limit").value;
// check input
if (searchTerm === "") {
// show message
showMessage("Please add a search Term!", "alert-danger");
}
// clear input
searchInput.value = "";
// search reddit
reddit.search(searchTerm, searchLimit, sortBy).then(results => {
console.log(results);
});
});
fetch returns a promise, but search doesn't have any return statement at all.
After you call catch on it, the promise is discarded.
If you want to use it outside the search function then you need to return it.
If you intend to do that
reddit.search(searchTerm, searchLimit, sortBy)
.then(results => {
console.log(results);
})
.catch(error => {
console.log(error);
});
you should wrap fetch inside a Promise.
example:
export default {
search: (searchTerm, searchLimit, sortBy) => {
return new Promise((resolve, reject) => {
fetch(`http://www.reddit.com/search.json?q=${searchTerm}&sort=${sortBy}&limit=${searchLimit}`)
.then(res => res.json())
.then(data => resolve(data.data.children.map(data => data.data)))
.catch(err => reject(err));
});
}
};
I have a situation when I need 2 Redux Actions to be run consecutively.
The context is a user clicks on a Preview button, and I want to display a loader until the puzzle is done generating.
function mapDispatchToProps(dispatch) {
return {
onPreview: () => {
dispatch(generatePreview());
},
};
}
In order to do it, I use the middleware redux-thunk and the action I want to be executed first returns a Promise.resolve() and my second action is in the then():
export function generatingPreview() {
return dispatch => {
dispatch({
type: GENERATING_PREVIEW,
});
return Promise.resolve();
};
}
export function generatePreview() {
return (dispatch, getState) => {
dispatch(generatingPreview()).then(() => {
const state = getState();
const conf = state.getIn(['login', 'conf']).toJS();
const wordList = state.getIn(['login', 'wordList']);
try {
const newPuzzle = Wordfind.newPuzzleLax(wordList, conf);
dispatch(generatePreviewSuccess(newPuzzle));
} catch (err) {
dispatch(generatePreviewError(err.message));
}
});
};
}
export function generatePreviewError(error) {
return {
type: GENERATE_PREVIEW_ERROR,
error,
};
}
export function generatePreviewSuccess(payload) {
return {
type: GENERATE_PREVIEW_SUCCESS,
payload,
};
}
Unfortunately, the loader never appears. I console.logged the state setting the loading to true when my component renders, and it changes! I can see the log but not the loader, the component doesn't really re-render until the actions generatePreviewSuccess() or generatePreviewError() are dispatched. And it's not an issue from the loader, if I replace the newPuzzleLax function by a loop in order to make enough time to see it, I can see it!
My theory is this function Wordfind.newPuzzleLax(wordList, conf) that I use to generate the puzzle is blocking the queue of actions because on the Chrome Redux Tools I an see the first action appearing at the same time that the second one:
Link to the function.
If I add a 1-microsecond delay between the dispatch of the two actions, the loader appears... but I would really like to understand what is happening. Thank you in advance. If it's any help, I use the react-boilerplate
I also tried to transform the function generating the puzzle as an async one by doing this:
const wordFindAsync = async (wordList, conf) =>
Wordfind.newPuzzleLax(wordList, conf);
export function generatePreview() {
return (dispatch, getState) => {
dispatch(generatingPreview())
.then(() => {
const state = getState();
const conf = state.getIn(['login', 'conf']).toJS();
const wordList = state.getIn(['login', 'wordList']);
wordFindAsync(wordList, conf);
})
.then(res => dispatch(generatePreviewSuccess(res)))
.catch(err => {
dispatch(generatePreviewError(err.message));
});
};
}
In your second version you're not returning the Promise from wordFindAsync(wordList, conf) back into your original Promise chain, and so its not being resolved/waited on by then next then.
export function generatePreview() {
return (dispatch, getState) => {
dispatch(generatingPreview())
.then(() => {
const state = getState();
const conf = state.getIn(['login', 'conf']).toJS();
const wordList = state.getIn(['login', 'wordList']);
return wordFindAsync(wordList, conf); // ๐ return your promise here
})
.then(res => dispatch(generatePreviewSuccess(res)))
.catch(err => {
dispatch(generatePreviewError(err.message));
});
};
}
Here's a simple example demoing the behavior I'm refering to.
This one will only wait 1 second until logging "done":
const waitOneSec = () =>
new Promise(resolve => {
console.log("waiting 1 secoond");
setTimeout(resolve, 1000);
});
waitOneSec()
.then(() => {
waitOneSec(); // Promise not returned
})
.then(() => console.log("done"));
Whereas this one will wait full 2 seconds until logging "done":
const waitOneSec = () =>
new Promise(resolve => {
console.log("waiting 1 secoond");
setTimeout(resolve, 1000);
});
waitOneSec()
.then(() => {
return waitOneSec(); // ๐ Promise returned
})
.then(() => console.log("done"));
Hope that helps.
Please save my sanity. I'm trying to do some error handling inside my observable. I've followed heaps of articles but it hasn't clicked. I'd rather handle errors inside the service and push out the cached data if an error occurs.
I do this inside my angular2 component...
private initializeJobPolling() {
this.subscription = Observable
.interval(5000)
.startWith(0)
.flatMap(() => {
return this.jobService.getJobs();
})
.subscribe(
(jobsContainer: any) => {
let allJobs: IJob[] = jobsContainer.jobs.map(j => new Job(j));
this.allJobs = allJobs;
} );
}
and inside jobService...
getJobs() {
let thisService = this;
return thisService.http.get('app/services/jobs.json')
.map((responseData) => {
console.log('getting Jobs from API');
thisService.allJobs = responseData.json();
return thisService.allJobs;
});
}
Use catch:
getJobs() {
return this.http.get('app/services/jobs.json')
.map((responseData) => {
console.log('getting Jobs from API');
this.allJobs = responseData.json();
return this.allJobs;
})
.catch(err => {
console.error(err);
return Observable.just(this.allJobs);
});
}
Update:
As pointed out in the comments, RxJS 5 doesn't have .just, so use .of instead:
getJobs() {
return this.http.get('app/services/jobs.json')
.map((responseData) => {
console.log('getting Jobs from API');
this.allJobs = responseData.json();
return this.allJobs;
})
.catch(err => {
console.error(err);
return Observable.of(this.allJobs);
});
}