admin管理员组文章数量:1323188
Background of the Problem:
I am building a React/Redux app that uses redux-thunk and wretch (a fetch wrapper) to handle asynchronous requests.
I have a few search actions that can vary significantly in their load times, causing undesirable behavior.
I have looked into using AbortController(), but it's either cancelling all my requests outright, or failing to cancel the previous request.
example problem:
- Request a search for "JOHN", then request a search for "JOHNSON".
- Results for "JOHNSON" return first, and then results for "JOHN" return later and overwrite the "JOHNSON" results.
Goal:
Initiating a request should abort previous pending requests.
example desired behavior:
- Request a search for "JOHN", then request a search for "JOHNSON".
- Upon initiating the request for "JOHNSON", the pending request for "JOHN" is aborted.
Code:
actions.js
The fetchData action gets called via an onClick or by other functions.
import api from '../../utils/request';
export function fetchData(params) {
return dispatch => {
dispatch(requestData());
return api
.query(params)
.url('api/data')
.get()
.fetchError(err => {
console.log(err);
dispatch(apiFail(err.toString()));
})
.json(response => dispatch(receiveData(response.items, response.totalItems)))
}
}
export function requestData() {
return {
type: REQUEST_DATA,
waiting: true,
}
}
export function receiveData(items, totalItems) {
return {
type: RECEIVE_DATA,
result: items,
totalItems: totalItems,
waiting: false,
}
}
export function apiFail(err) {
return {
type: API_FAIL,
error: err,
waiting: false,
}
}
utils/request.js
This is wretch import. Wretch is a fetch wrapper so it should function similarly to fetch.
import wretch from 'wretch';
/**
* Handles Server Error
*
* @param {object} err HTTP Error
*
* @return {undefined} Returns undefined
*/
function handleServerError(err) {
console.error(err);
}
const api = wretch()
.options({ credentials: 'include', mode: 'cors' })
.url(window.appBaseUrl || process.env.REACT_APP_API_HOST_NAME)
.resolve(_ => _.error(handleServerError))
export default api;
Attempt:
I've tried using wretch's .signal() parameter with an AbortController(), calling .abort() after the request, but that aborts all requests, causing my app to break. Example below:
import wretch from 'wretch';
/**
* Handles Server Error
*
* @param {object} err HTTP Error
*
* @return {undefined} Returns undefined
*/
function handleServerError(err) {
console.error(err);
}
const controller = new AbortController();
const api = wretch()
.signal(controller)
.options({ credentials: 'include', mode: 'cors' })
.url(window.appBaseUrl || process.env.REACT_APP_API_HOST_NAME)
.resolve(_ => _.error(handleServerError))
controller.abort();
export default api;
I've tried moving the logic around to various places, but it seems abort all actions or abort none of them.
Any advice as to how to go about this would be appreciated, this is critical for my team.
Thank you
Background of the Problem:
I am building a React/Redux app that uses redux-thunk and wretch (a fetch wrapper) to handle asynchronous requests.
I have a few search actions that can vary significantly in their load times, causing undesirable behavior.
I have looked into using AbortController(), but it's either cancelling all my requests outright, or failing to cancel the previous request.
example problem:
- Request a search for "JOHN", then request a search for "JOHNSON".
- Results for "JOHNSON" return first, and then results for "JOHN" return later and overwrite the "JOHNSON" results.
Goal:
Initiating a request should abort previous pending requests.
example desired behavior:
- Request a search for "JOHN", then request a search for "JOHNSON".
- Upon initiating the request for "JOHNSON", the pending request for "JOHN" is aborted.
Code:
actions.js
The fetchData action gets called via an onClick or by other functions.
import api from '../../utils/request';
export function fetchData(params) {
return dispatch => {
dispatch(requestData());
return api
.query(params)
.url('api/data')
.get()
.fetchError(err => {
console.log(err);
dispatch(apiFail(err.toString()));
})
.json(response => dispatch(receiveData(response.items, response.totalItems)))
}
}
export function requestData() {
return {
type: REQUEST_DATA,
waiting: true,
}
}
export function receiveData(items, totalItems) {
return {
type: RECEIVE_DATA,
result: items,
totalItems: totalItems,
waiting: false,
}
}
export function apiFail(err) {
return {
type: API_FAIL,
error: err,
waiting: false,
}
}
utils/request.js
This is wretch import. Wretch is a fetch wrapper so it should function similarly to fetch.
import wretch from 'wretch';
/**
* Handles Server Error
*
* @param {object} err HTTP Error
*
* @return {undefined} Returns undefined
*/
function handleServerError(err) {
console.error(err);
}
const api = wretch()
.options({ credentials: 'include', mode: 'cors' })
.url(window.appBaseUrl || process.env.REACT_APP_API_HOST_NAME)
.resolve(_ => _.error(handleServerError))
export default api;
Attempt:
I've tried using wretch's .signal() parameter with an AbortController(), calling .abort() after the request, but that aborts all requests, causing my app to break. Example below:
import wretch from 'wretch';
/**
* Handles Server Error
*
* @param {object} err HTTP Error
*
* @return {undefined} Returns undefined
*/
function handleServerError(err) {
console.error(err);
}
const controller = new AbortController();
const api = wretch()
.signal(controller)
.options({ credentials: 'include', mode: 'cors' })
.url(window.appBaseUrl || process.env.REACT_APP_API_HOST_NAME)
.resolve(_ => _.error(handleServerError))
controller.abort();
export default api;
I've tried moving the logic around to various places, but it seems abort all actions or abort none of them.
Any advice as to how to go about this would be appreciated, this is critical for my team.
Thank you
Share Improve this question asked Feb 20, 2020 at 20:13 PepPep 3872 gold badges8 silver badges17 bronze badges 2- This is something that sagas do really well: redux-saga.js – Ed Lucas Commented Feb 21, 2020 at 3:43
- I appreciate the input, but I fear that switching over to redux-saga will be too much of a rebuild given our limited time and resources. – Pep Commented Feb 21, 2020 at 15:44
1 Answer
Reset to default 5I feel pretty silly right now, but this is what it took to get it working.
Solution Steps:
- Set an AbortController to the initialState of the reducer
reducer.js
export default (state = {
controller: new AbortController(),
}, action) => {
switch (action.type) {
...
- Get the AbortController from the state, at the beginning of the fetch action and abort it.
- Create a new AbortController and pass it into the requestData action.
- Pass the new AbortController into the
signal()
param of the wretch call.
actions.js
export function fetchData(params) {
return (dispatch, getState) => {
const { controller } = getState().reducer;
controller.abort();
const newController = new AbortController();
dispatch(requestData(newController));
return api
.signal(newController)
.query(params)
.url('api/data')
.get()
.fetchError(err => {
console.log(err);
dispatch(apiFail(err.toString()));
})
.json(response => dispatch(receiveData(response.items, response.totalItems)))
}
}
export function requestData(controller) {
return {
type: REQUEST_DATA,
waiting: true,
controller,
}
}
In the reducer, for the case of the requestData action, set the new AbortController to the state.
reducer.js
case REQUEST_DATA:
return {
...state,
waiting: action.waiting,
controller: action.controller
};
There's some additional functionality with wretch, an .onAbort()
param, that allows you to dispatch other actions when the request is aborted. I haven't coded that out yet, but I figured I'd include the info for anyone else struggling with this.
本文标签: javascriptCancel previous fetch request with reduxthunkStack Overflow
版权声明:本文标题:javascript - Cancel previous fetch request with redux-thunk - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742128407a2422048.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论