Hi - I looked at your program, it doesn't work because the request is never cleaned up. My post talks about automatically clean up requests with useEffect indeed it might not be so easy to work with for POST/PUT, etc, or requests that only fire on user action (not via an effect).
Your code, uses useCallback which is just a simple memoizer. the return at the end won't clean it up for you. We can rewrite it to a function creator that returns a function that automatically cleans up its previous request when called again:
constpostList=makeRequester();// we have to create the function first.// later...consthandleFormSubmit=()=>{// ... gather newEntry data herepostList(newEntry);}
this function will only allow one request being made at the same time - it doesn't cancel the request for you when the component unmounts though, to do that, we should move cancelToken to a ref, here's a possible implementation for that:
// in the componentconstcancelToken=React.useRef(null);// this effect cancels the requests when the component unmounts;React.useEffect(()=>{return()=>{if(cancelToken.current){cancelToken.current.cancel();}};},[]);constpostList=async(entry)=>{// cancels the previous requestif(cancelToken.current){cancelToken.current.cancel();}// creates a new token cacelToken.current=axios.CancelToken.source();dispatch(loading());try{// ... same as the example above}catch(e){// ... same }}
now we don't need to "make" a new requester anymore, to use it we can call it directly with the new "entry". This postList function automatically cancels its previous request when called again, and if there are any pending requests, they will be canceled when the component unmounts.
I'm using the custom hooks as values to the context api, here's the main component where the list component is unmounted during the "isLoading" phase, as you can see the get request is inside the useEffect:
Hi - that's a lot of code - i took a quick look, one thing I probably didn't explain well with my first example is that the makeRequest method returns a function that makes requests when called. (i know it's a bit confusing)
in your example, your getRequest or postRequest methods are factories - to use them, you have to do something like:
// in the componentconst{state,getRequest}=useContext(AppContext);constgetList=React.useRef(getRequest());// note that `getRequest` is called right awayuseEffect(()=>{getList.current();// calls on mount},[]);
please try to follow my 2nd example as it cleans up the requests when component unmounts. I'd recommend trying to start small, don't try to get everything working in one go, instead, try to focus on only 1 method, and get it to work correctly (and tested) first.
The new postRequest could look something like this:
Please note this method is very different from the first one - to use it, do something like this in the component:
const{postRequest}=useContext(AppContext);constcancelToken=React.useRef(null);// this effect cancels the requests when the component unmounts;React.useEffect(()=>{return()=>{if(cancelToken.current){cancelToken.current.cancel();}};},[]);constcreateNewList=(entry)=>{// cancels the previous requestif(cancelToken.current){cancelToken.current.cancel();}// creates a new token cacelToken.current=axios.CancelToken.source();postRequest(entry,cancelToken.current.token);}consthandleSubmit=(e)=>{e.preventDefault();createNewList(newList);exitHandler();};
there were a couple of other issues with your code, for exmaple, axios.post takes configuration as the 3rd parameter, not the 2nd; and in your catch blocks axios.isCancel means the request was canceled (instead of encountered an error) - usually we want to handle error when the request was NOT canceled.
Anyways, try to get a single request working properly first before trying to optimize or generalize your use case, don't worry about separating functionality or abstraction at this stage.
Hi - I looked at your program, it doesn't work because the request is never cleaned up. My post talks about automatically clean up requests with
useEffect
indeed it might not be so easy to work with for POST/PUT, etc, or requests that only fire on user action (not via an effect).Your code, uses
useCallback
which is just a simple memoizer. thereturn
at the end won't clean it up for you. We can rewrite it to a function creator that returns a function that automatically cleans up its previous request when called again:to use it:
this function will only allow one request being made at the same time - it doesn't cancel the request for you when the component unmounts though, to do that, we should move
cancelToken
to aref
, here's a possible implementation for that:now we don't need to "make" a new requester anymore, to use it we can call it directly with the new "entry". This
postList
function automatically cancels its previous request when called again, and if there are any pending requests, they will be canceled when the component unmounts.I followed the new one but i couldn't make any request for the first approach, It's a simple mern stack, I'm using context api + useReducer:
Actions:
Reducer:
here's my custom hook for all 5 types of api request:
I'm using the custom hooks as values to the context api, here's the main component where the list component is unmounted during the "isLoading" phase, as you can see the get request is inside the useEffect:
Here's one of the modals for event driven requests like post:
Hi - that's a lot of code - i took a quick look, one thing I probably didn't explain well with my first example is that the
makeRequest
method returns a function that makes requests when called. (i know it's a bit confusing)in your example, your
getRequest
orpostRequest
methods are factories - to use them, you have to do something like:please try to follow my 2nd example as it cleans up the requests when component unmounts. I'd recommend trying to start small, don't try to get everything working in one go, instead, try to focus on only 1 method, and get it to work correctly (and tested) first.
The new
postRequest
could look something like this:Please note this method is very different from the first one - to use it, do something like this in the component:
there were a couple of other issues with your code, for exmaple,
axios.post
takes configuration as the 3rd parameter, not the 2nd; and in yourcatch
blocksaxios.isCancel
means the request was canceled (instead of encountered an error) - usually we want to handle error when the request was NOT canceled.Anyways, try to get a single request working properly first before trying to optimize or generalize your use case, don't worry about separating functionality or abstraction at this stage.
Hi sorry for the late reply, I followed your suggestions and here's what my app can do:
Here's my updated custom hook that has all the factory requests:
I tried negating the: axios.isCancel(err) but to no avail, here's my api request codes.
GET Request:
POST Request:
DELETE Request:
hi @paolo - sorry I just saw this. Could you setup a github repo or add me to your existing one? my github handle is @pallymore
alternatively could you set this up on codesandbox.io ? it'll be easier to read/write code there, thanks!
Thanks so much, I added you on github :)