One thing that has struck me with Javascript’s fetch function is that it often looks so simple.
fetch('/something.json').then(res=>res.json()).then(json=>{// do something useful here with json...
});
When I see the same thing in a pull request, I’m not overly impressed. No, just because this works on a sunny day doesn’t mean you’re done. You need error handling as well! Being explicit about how to handle errors is so much better than giving users an infinite spinner to stare at.
Since fetch doesn’t throw you into the catch clause for non-2xx responses, you need to check the ok property or check status for a specific status code. But both then and catch can use the same function for error handling.
letisLoading=true;lethasError=false;letdata={};functionhandleFetchResponse(response){hasError=!response.ok;isLoading=false;returnresponse.ok&&response.json?response.json():data;}functionfetchData(){returnfetch(url).then(handleFetchResponse).catch(handleFetchResponse);}fetchData().then(data=>{// do something useful here with data...
});
Of course it all depends on your application, but to me this is minimal error handling. To have it be used by the team throughout an application, I’ve found it necessary to encapsulate it into a reusable function. I’m currently working in a React code base, so this is the custom hook I wrote.
import{useEffect,useState}from"react";/* Example
initialUrl: "/_api/jobs"
initialData: [] //usually empty array or object
*/exportconstuseOurApi=(initialUrl,initialData)=>{const[url,setUrl]=useState(initialUrl);const[isLoading,setIsLoading]=useState(true);const[hasError,setHasError]=useState(false);const[fetchedData,setFetchedData]=useState(initialData);useEffect(()=>{letunmounted=false;consthandleFetchResponse=response=>{if(unmounted)returninitialData;setHasError(!response.ok);setIsLoading(false);returnresponse.ok&&response.json?response.json():initialData;};constfetchData=()=>{setIsLoading(true);returnfetch(url,{credentials:'include'}).then(handleFetchResponse).catch(handleFetchResponse);};if(initialUrl&&!unmounted)fetchData().then(data=>!unmounted&&setFetchedData(data));return()=>{unmounted=true;};},[url]);return{isLoading,hasError,setUrl,data:fetchedData};};
This way, you get an error indicator and a loading indicator out-of-the-box when using this data fetching function. Used like this in a (simplified) Jobs.jsx.
importReactfrom"react";import{useOurApi}from"../Common/Services/HttpService";import{Spinner}from"../Common/Components/Spinner";import{ErrorMessage}from"../Common/Components/ErrorMessage";import{JobFeed}from"./JobFeed";exportdefaultfunctionJobs(){consturl=`/_api/jobs`;const{data,isLoading,hasError}=useOurApi(url,{});if(isLoading)return<Spinner/>;if(hasError)return<ErrorMessagemessage={`Failed to fetch open jobs 😟`}/>;return(<divclassName="our-grid"><JobFeedjobs={data}/></div>);}
No matching posts found. You can use wildcards and search only in titles, e.g. title:iot