I have managed to cancel my http request via axios and redux.
This is what I have so far. I have a modal, this modal is saving data to a database, once I click on the abort, i cancel the dispatch action (the axios call made to save data) in the useEffect. Let 's say now the user exit the modal and decide some other time to go back on the modal to save its data again, the cancel signal is still on the route. How do I clear up the abort controller ? is there a way to do a clean up ?
Here is my service file :
export const sync = (l, obj, pId, controller) => {
let a = { ...obj };
return axios.post(`/${l}/f/ff/Create`, {
signal: controller.signal
});
};
my action file
export const save =
(l, el, pId,controller ) => async (dispatch) => {
try {
dispatch({ type: SAVE_REQUEST });
await sync(l, el, pId, controller);
dispatch({
type: SAVE_SUCCESS,
payload: el,
});
} catch ( error) {
dispatch({ type: SAVE_FAIL, payload: error});
}
}
};
and my component
const { current: controller } = useRef(null);
const handleAbort = () => {
controller.current?.abort();
controller.current = null;
};
useEffect(() => {
controller.current = new AbortController();
dispatch(save(l,el,p.id,controller)
return () => {
handleAbort();
};
},[])
<div>
<Modal>
<progressbar/>
<button onclick={handleAbort()}>abort</button>
<button >exit</button>
</Modal>
</div>
Related
Im trying to debounce a api action which is a dispatch call to reducer.The api call in the browser should debounce after a particular delay given as a single api , but its going as multiple api calls after the delay ,the code is as follows.
please refer the screenshot also
const apiCall = (args) => {
dispatch(getECByStatus({status: 'PENDING_APPROVAL', search: args}))
}
const debounce = (apiFunc, delay) => {
let inDebounce
return function () {
const context = this
const args = arguments
clearTimeout(inDebounce)
inDebounce = setTimeout(() => {
inDebounce = null
apiFunc.apply(context, args)
},delay);
}
}
const optimizedVersion = debounce(apiCall, 600)
const handleSearchChange = (value) => {
optimizedVersion(value)
}
the handleSearchChange is the onchange event fired from the input box on typing the input.getECByStatus is a redux action creator, which calls api with the search param,
export const getECByStatus = (params) => async (dispatch) => {
let editCheckType = params?.type ? `/${params.type}` : ''
let searchParams = params?.search ? `&search=${params.search}` : ''
try {
dispatch({
type: actionType.GET_EC_BY_STATUS_REQUEST,
payload: {
load: true,
},
})
let study_id = getItem('study_id')
const { data } = await DataService.get(
`/edit-checks${editCheckType}?status=${params.status}&study_id=${study_id}${searchParams}`
)
dispatch({
type: actionType.GET_EC_BY_STATUS_SUCCESS,
payload: {
load: false,
data: data.data,
}
},
})
} catch (error) {
console.error('Get EC by status error', error)
dispatch({
type: actionType.GET_EC_BY_STATUS_FAIL,
payload: {
load: false,
},
})
}
}
Thanks in advance!
This is due to the UI re-render after dispatch event get fired, since the input box is a controlled one , we have to pass the updated value to the input box component, so we can make pass a ref for not re-rendering the UI.
I have a const that assembles a get (in another component) and returns me a verification code, this is: response.data.verifyCode
This component is called through a submit on another component.
I need to get this value in my another const, which is below:
export const sendCode = (id, username) => (dispatch) => {
dispatch({ some code here });
return registerAccount
.sendCode(id, username)
.then((response) => {
dispatch({ payload: response.data.verifyCode });
return response.data;
})
.catch(() => {
return null;
});
};
export const getCodeAndVerify = (id, userCode) => (dispatch) => {
dispatch({ some code here });
const getVerifyCode = // I need to get response.data.verifyCode from sendCode above
// I try to use
// const getVerifyCode = { verifyCode: sendCode() };
// but this returns [object object]
return registerAccount
.getCodeAndVerify(id, userCode, getVerifyCode)
.then(() => {
// some code here
})
.catch(() => {
// some code here
});
};
That is, I need to get the verifyCode from the return from the superior const and use it in the other const, but I'm not sure how to do that. Can someone help me?
Asynchronous actions (I'm assuming thunks) also receive a getState second argument after dispatch. Assuming there's a reducer to handle the verifyCode send code success, you can access the store and retrieve the verifyCode value in getCodeAndVerify.
export const sendCode = (id, username) => (dispatch) => {
dispatch({ some code here });
return registerAccount
.sendCode(id, username)
.then((response) => {
dispatch({
type: 'VERIFY_CODE_SUCCESS', // <-- action object needs type
payload: response.data.verifyCode,
});
return response.data;
})
.catch(() => {
return null;
});
};
export const getCodeAndVerify = (id, userCode) => async (dispatch, getState) => {
dispatch({ type: TYPES.PASS_CREATION_REQUESTED });
const getVerifyCode = getState().path.to.verifycode; // <-- retrieve from state
return registerAccount
.getCodeAndVerify(id, userCode, getVerifyCode)
.then(() => {
// some code here
})
.catch(() => {
// some code here
});
};
I have app wrote on pure React where I make request to server and get response - category list. But I needed to rework small part of my app.
But when I remake this part my list dissapeared.
I tried write console.log("My data look like:", data); after const data = await api('pathWithQueryParams', {. to see if the data is coming. But I don't even see the text My data look like: in browser console. That is, in inside the function fetchData even console.log dont work.
First in my question I'll write code that I remake to Redux
and below after _______________________________
I'll write small part my app which wrote on pure React(before remake to redux) and work well.
Wrote on REDUX:
Home.js:
const Home = () => {
const listCategory = useSelector(state => state.filterListReducer.listCategory);
const currentPage = useSelector(state => state.filterListReducer.currentPage);
const quantityElementPage = useSelector(state => state.filterListReducer.quantityElementPage);
const sortAscDesc = useSelector(state => state.filterListReducer.sortAscDesc);
const searchInput = useSelector(state => state.filterListReducer.searchInput);
useEffect(() => {
fetchData(currentPage, quantityElementPage, sortAscDesc, searchInput);
}, [currentPage, quantityElementPage, sortAscDesc, searchInput]);
async function fetchData(valuePage, valueElement, valueSort, valueFilter, dispatch ) {
return async (dispatch) => {
try {
dispatch({ type: "LOAD_DATA_START" });
const data = await api(`pathWithQueryParams`, { // <-- api - it function which using fetch make request to server
method: 'GET',
});
console.log("My data look like:", data); // <-- check if the data came in response, but I don't see even text "My data look like:" in browser console
dispatch({ type: "LOAD_DATA_END", payload: data });
} catch (e) {
console.error(e);
}
};
}
return ( <div> <Table dataAttribute={listCategory} /> </div> ); };
___________________________________________________
Wrote on pure React (before remake to redux):
const Home = () => {
const [value, setValue] = useState({
listCategory: [],
currentPage: 1,
quantityElementPage: 3,
buttonsPagination: 0,
buttonsQuantityElementPage: 3,
sortAscDesc: "asc",
searchInput: ""
});
useEffect(() => {
fetchData(value.currentPage, value.quantityElementPage, value.sortAscDesc, value.searchInput);
}, [value.currentPage, value.quantityElementPage, value.sortAscDesc, value.searchInput]);
async function fetchData(valuePage, valueElement, valueSort, valueFilter ) {
try {
const data = await api(`pathWithQueryParams`, {
method: 'GET',
});
setValue(prev => ({
...prev,
listCategory: data.data,
currentPage: data.page,
buttonsPagination: Math.ceil(data.total / data.perPage),
quantityElementPage: data.perPage,
}));
} catch (e) {
console.error(e);
}
}
// Home.js fragment
useEffect(() => {
/*
fetchData(currentPage, quantityElementPage, sortAscDesc, searchInput);
invoke fetchData, but does not call returning function
e.g. const actualFetching = fetchData(currentPage, quantityElementPage, sortAscDesc, searchInput);
and missing call actualFetching(dispatch);
*/
fetchData(currentPage, quantityElementPage, sortAscDesc, searchInput)(dispatch);
}, [currentPage, quantityElementPage, sortAscDesc, searchInput]);
async function fetchData(valuePage, valueElement, valueSort, valueFilter
/*, dispatch - not necessary here */ ) {
/*
returning function (dispatch) => {}
*/
return async (dispatch) => {
try {
dispatch({ type: "LOAD_DATA_START" });
const data = await api(`pathWithQueryParams`, { // <-- api - it function which using fetch make request to server
method: 'GET',
});
console.log("My data look like:", data); // <-- check if the data came in response, but I don't see even text "My data look like:" in browser console
dispatch({ type: "LOAD_DATA_END", payload: data });
} catch (e) {
console.error(e);
}
};
}
async function fetchData(valuePage, valueElement, valueSort, valueFilter, dispatch ) {
return async (dispatch) => {
try {
dispatch({ type: "LOAD_DATA_START" });
const data = await api(`pathWithQueryParams`, { // <-- api - it function which using fetch make request to server
method: 'GET',
});
console.log("My data look like:", data); // <-- check if the data came in response, but I don't see even text "My data look like:" in browser console
dispatch({ type: "LOAD_DATA_END", payload: data });
} catch (e) {
console.error(e);
}
};
}
This function is now returning a function, I think you are trying to create a hook here so the way to probably do this is:
useEffect(() => {
const fetch = fetchData();//this returns a function now
fetch(currentPage, quantityElementPage, sortAscDesc, searchInput);
}, [currentPage, quantityElementPage, sortAscDesc, searchInput]);
function fetchData() {
return async (valuePage, valueElement, valueSort, valueFilter, dispatch) => {
try {
dispatch({ type: "LOAD_DATA_START" });
const data = await api(`pathWithQueryParams`, {
// <-- api - it function which using fetch make request to server
method: "GET"
});
console.log("My data look like:", data); // <-- check if the data came in response, but I don't see even text "My data look like:" in browser console
dispatch({ type: "LOAD_DATA_END", payload: data });
} catch (e) {
console.error(e);
}
};
}
I have a simple react-native application with a redux store set up. Basically I want to add a new story, dispatch the redux action and transition to this new story after it has been created.
I have the following code in my Container Component, which runs when the user taps on an add button.
addStory() {
this.props.actions.stories.createStory()
.then(() => Actions.editor({ storyId: last(this.props.stories).id }); // makes the transition)
}
And the following action creator.
export const createStory = () => (dispatch) => {
dispatch({ type: CREATE_STORY, payload: { storyId: uniqueId('new') } });
return Promise.resolve();
};
As you see, I return a promise in the action creator. If I don't return a promise here, the transition will be made before the state has been updated.
This seems a little odd to me - why do I have to return a resolved Promise here? Aren't dispatches meant to be synchronous?
As discussed in comments
Callbacks Example:
addStory() {
this.props.actions.stories.createStory( (id) => {
Actions.editor({ storyId: id })
});
}
export const createStory = ( callback ) => (dispatch) => {
const _unique_id = uniqueId('new');
dispatch({ type: CREATE_STORY, payload: { storyId: _unique_id } });
callback(_unique_id);
};
Timeout Example:
Here we're assuming the state would have updated by now.. that's not the case most of the times.
addStory() {
this.props.actions.stories.createStory()
setTimeout( () => {
Actions.editor({ storyId: last(this.props.stories).id });
}, 500);
}
export const createStory = () => (dispatch) => {
dispatch({ type: CREATE_STORY, payload: { storyId: uniqueId('new') } });
};
Promise:
this can take a sec or a minute to complete.. it doesn't matter. you do everything you have to do here and finally resolve it so the app/component can perform next actions.
export const createStory = () => (dispatch) => {
return new Promise( (resolve, reject) => {
// make an api call here to save data in server
// then, if it was successful do this
dispatch({ type: CREATE_STORY, payload: { storyId: uniqueId('new') } });
// then do something else
// do another thing
// lets do that thing as well
// and this takes around a minute, you could and should show a loading indicator while all this is going on
// and finally
if ( successful ) {
resolve(); // we're done so call resolve.
} else {
reject(); // failed.
}
});
};
And now, checkout http://reactivex.io/rxjs/
I have an async action, which fetch data from REST API:
export const list = (top, skip) => dispatch => {
dispatch({ type: 'LIST.REQUEST' });
$.get(API_URL, { top: top, skip: skip })
.done((data, testStatus, jqXHR) => {
dispatch({ type: 'LIST.SUCCESS', data: data });
});
};
A sync action, which changes skip state:
export const setSkip = (skip) => {
return {
type: 'LIST.SET_SKIP',
skip: skip
};
};
Initial state for top = 10, skip = 0. In component:
class List extends Component {
componentDidMount() {
this.list();
}
nextPage() {
let top = this.props.list.top;
let skip = this.props.list.skip;
// After this
this.props.onSetSkip(skip + top);
// Here skip has previous value of 0.
this.list();
// Here skip has new value of 10.
}
list() {
this.props.List(this.props.list.top, this.props.list.skip);
}
render () {
return (
<div>
<table> ... </table>
<button onClick={this.nextPage.bind(this)}>Next</button>
</div>
);
}
}
When button Next at first time clicked, value of skip which uses async action not changed.
How I can to dispatch action after sync action?
If you are using redux thunk, you can easily combine them.
It's a middleware that lets action creators return a function instead of an action.
Your solution might have worked for you now if you don't need to chain the action creators and only need to run both of them.
this.props.onList(top, newSkip);
this.props.onSetSkip(newSkip);
If you need chaining(calling them in a synchronous manner) or waiting from the first dispatched action's data, this is what I'd recommend.
export function onList(data) {
return (dispatch) => {
dispatch(ONLIST_REQUEST());
return (AsyncAPICall)
.then((response) => {
dispatch(ONLIST_SUCCESS(response.data));
})
.catch((err) => {
console.log(err);
});
};
}
export function setSkip(data) {
return (dispatch) => {
dispatch(SETSKIP_REQUEST());
return (AsyncAPICall(data))
.then((response) => {
dispatch(SETSKIP_SUCCESS(response.data));
})
.catch((err) => {
console.log(err);
});
};
}
export function onListAndSetSkip(dataForOnList) {
return (dispatch) => {
dispatch(onList(dataForOnList)).then((dataAfterOnList) => {
dispatch(setSkip(dataAfterOnList));
});
};
}
Instead of dispatching an action after a sync action, can you just call the function from the reducer?
So it follows this flow:
Sync action call --> Reducer call ---> case function (reducer) ---> case function (reducer)
Instead of the usual flow which is probably this for you:
Sync action call --> Reducer call
Follow this guide to split the reducers up to see what case reducers are.
If the action you want to dispatch has side affects though then the correct way is to use Thunks and then you can dispatch an action after an action.
Example for Thunks:
export const setSkip = (skip) => {
return (dispatch, getState) => {
dispatch(someFunc());
//Do someFunc first then this action, use getState() for currentState if you want
return {
type: 'LIST.SET_SKIP',
skip: skip
};
}
};
also check this out redux-sequence-action
Thanks for the replies, but I made it this way:
let top = this.props.list.top;
let skip = this.props.list.skip;
let newSkip = skip + top;
this.props.onList(top, newSkip);
this.props.onSetSkip(newSkip);
First I calculate new skip and dispatch an async action with this new value. Then I dispatch a syns action, which updates skip in state.
dispatch({ type: 'LIST.SUCCESS', data: data, skip: The value you want after sync action });