Custom fetch hook that works with React Suspense

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey it's Lee Halladay and we've got a cool video right now where we're going to be showing how to build a custom hook and it's gonna be called use fetch suspense because it's basically going to be a hook to help us make fetch calls but it's going to be done in such a way that you can use it along with suspense to handle what to display when the fetch request is being performed so I this is really part three of a small four-part series of demos where I'm showing on the first two parts the basics of of suspense where I'm lazy loading the nav and each different page as I click around using reach router so in this one we're going to be working on the home page to create our custom hook and use it with suspense so let's get started close this all right so here's our app so we're going to be working on the homepage which is routed and displayed when the path is slash so I'm just going to close everything else because it's not needed and this is where we're going to be working and just to show the data we're going to be fetching and displaying I found this little API it basically shows all the different subway routes for for Boston I'm not from there I've never been there but it's open and it's not behind cores or anything like that so it's really sweet in my books so this is what we'll be working with today so we'll come back here and the first thing we're going to do is we'll just create a component and we'll call it the Boston routes dot J s and this will be just a simple component functional style component so we can use hooks so import react from react we'll have Boston roads which will return data ah done very nice so export this export default Boston routes and then let's use it just so we can make sure things are working as we're going through the implementation so we'll go back to home and we'll import Boston routes from Boston routes like that and maybe we'll make the home heaven to each one so we are home and then just below one we will put the Boston routes like that boat on Boston okay so we're home and we've got our data which hasn't been loaded yet so the first thing we are going to do is because we know we're going to need it is wrap this with suspense and if this you're starting from this video suspense is basically used to help you deal with asynchronous code in a more easy way without keeping track of all of your own loading States and stuff like that because Boston routes is going to be fetching an AJAX call which is asynchronous so what are we going to display while it's going to the server and fetching that data we obviously could handle it all ourselves but we're going to lean on suspense to help us out with that so with suspense you always have a fallback which is what to display while this asynchronous code hasn't yet been resolved for thinking in terms of promises which we should be and we just need it what to display here so I'm gonna I just call it loading data which will simply display a day of this as loading data so we'll come down here and put that in okay so it still works the suspense fallback is not yet being triggered because we haven't done anything asynchronous yet so let's get into building our custom hook so we're pretty much done in this file here suspense is wrapping our boss and routes so let's come into here and we will import the code that we're going to use and then we'll figure out the implementation for that so we will import something called use fetch suspense from use fetch suspense like that and what we'll do here is we'll say Const data equals use fetch suspense okay maybe it's a little bit weird that I haven't yet written this function but I've done this earlier in the day for an article so I sort of know where I'm going with this what it's going to return is some data that will then display out here for now we can just say let's stringify whatever we get back and embed that here okay so I'm going to be passing just two parameters to use fetch suspense the first is going to be that's failing the first is going to be the URL so we will copy that and we will pass that in as the first string and the second one is going to be I'm going to cut out all of those params and I'm going to be doing an object that has a query object where the first key will be this and its value will be that so for this API this basically I think 0 and 1 is just subway only there must be like a bunch of bus lines and stuff like that but this will return only I think it's like light and heavy rail or something like that okay so this is our our call we're passing in the request URL and any query parameter it could be headers or anything else you typically pass to fetch obviously fails because this code does not exist so let's go implement it so use fetch suspense is okay so we will start by defining our function so use fetch suspense be in arrow function and we're going to be exporting it as default okay so we said we are going to pass in the URL some fetch options which I guess theoretically could be empty if you're just fetching a straight-up URL and we'll leave that alone for now so just two parameters were passing in okay so now's where we have to deal with some of the suspense specific stuff that you wouldn't have to deal with if you were just sort of using fetch on your own so I built this diagram that shows how suspense works and I'll try to help use it as a tool to explain what we're doing so when react starts rendering our code it hits our suspense component so that would be out here suspense component like that so the first thing it does it basically checks is suspense awaiting a promise and if it is so if yes render the fallback so that's when it knows how to display this loading data if it is not awaiting a promise it keeps continuing rendering the code and it renders this asynchronous code so that would be our Boston routes but more specifically it's this use fetch suspense that will be doing something asynchronous so this asynchronous code it basically has to know is this the first time I'm being called and if it is it has to initiate the asynchronous code so how do we know whether it's the first render or not and we'll touch later why this is important because it sort of comes up to the top and rear Enders it and it hits this asynchronous code a second time and you don't want it to sort of fetch both times and get in this endless fetching loop so we need to keep track of whether or not we've made this request or not and to do that I'm going to use a small little caching library called LRU cache to basically keep track of the requests that's being made and whether it's the first time it's been called or subsequent times so we'll come back here and we will import LRU from LRU cache I think that's going to fail because I haven't added it to this to the library yet so I'm adding md5 as well because it's going to help me basically create my cash keys so that if I did have five different requests I could keep them keep track of them separately you okay so I've got LRU and we might as well bring in the md5 function right now as well because we're going to need it for our cache key and we'll create our cache here which will be new LRU and I think what you can pass in if we come here um LRU it stands for least recently used items so it's basically a cache that you can set up to say contain 50 things in total and it always gets rid of once it hits that 50 mark the things that are least recently accessed um so we can set it so that it doesn't keep growing and growing forever to have 50 items you could have at 500 items so you have to play with it to see what works for you but we'll just um just go with 50 because we're making one request so it doesn't matter okay so now that we have our cache what we're going to do is basically try to figure out a cache key and we're going to use the URL and the fetch options to determine that so we'll get a key and our key will be made up of let's say the URL that you are hitting dot and then we're going to take an md5 of all of the options passed in and use that as the second part of our cache key so the first thing we're going to do is we're going to stringify the fetch options and then we're going to take the md5 of that string of options okay so if I just console that logged the key see if it's working here we go so this is our key it's the URL we're hitting and then this md5 at the end okay so now that we have our key as I mentioned this code gets hit the first time and we need to determine have we ever made this request before so to do that we'll need to get the value out of the cache and we'll say cash dot get and you pass in the key that you want to read from the cache and because on the first call this nothing exists in the cache yet we're gonna have an or that has basically our default value for the first run and we'll say we'll give it a status of new and data will be null because we haven't actually perform the fetch yet okay so if we keep going along we need to determine whether it was the first request or the second request and I know a little bit further down the code say down here we're going to modify this value and set it to resolved so what we'll do is we'll say if the value does status is resolved we'll return that data so this will come into play later on but ignore that for now as we come down and we need to make the request for the first time so we look back to our diagram we've come down is this the first render it is and we know that because we've read the value from our cache using the key that we just constructed so now we need to initiate our synchronous encode so this is the actual fetch call that we're going to be making so we'll make a fetch to this URL given these fetch options and the first thing fetch or the thing to fetch gives you is a promise so we'll say then and we've got our response and we'll convert our response to JSON like that okay so typically you'd then add another then because you've got a second promise for when this first one is resolved but we are going to stop for now and we're actually going to put this into a variable so we've got this promise in a variable and there's a reason for that later on but let's just finish sort of dealing with our fetch promise right here so we can say so when promise dot then so when it finally resolves the request it converts it to JSON call this code and we've got our data so fetch is finished and we need to now update the value that was read from cache so we can say the status is now resolved we'll put the data from our fetch request into this value so data is now equal to whatever the fetch call give back to us and now we need to set this new cache so we'll use the key and we'll pass in the new value so this promise is now resolved so let's come down here we've updated our cache but here's now where suspense comes in so we initiated the asynchronous code and we dealt with it the next thing we need to do is throw a promise so probably have never done that before typically you throw errors but in JavaScript anything that is thrown can be caught so in this case we're going to be throwing this promise so if we follow our code down we've got suspense which calls Boston routes Boston routes calls use fetch responses use fetch suspense this code throws the promise and it is caught by suspense so we throw this promise we've saved the result to the cache expense bug cache wrong great um but basically when you throw this promise it gets thrown way up here and it gets caught by suspense so when suspense catches this promise it rear Enders so if we come back down in this loop again now it says are we awaiting the promise this time it will be yes because we've just thrown a promise up here that hasn't yet been resolved so now suspense knows to render our fallback so which will say loading data okay so it's actually working the whole but let's for a sec reload it you can see it's loading data for a second and then when the data gets filled in um it shows the actual result so what's happening here if we come back to this diagram um its rear-end 'red and it was awaiting the promise so it showed the fall back but 100 milliseconds later the promise that was caught by suspense up here was resolved so as soon as that happens suspense re-renders again so this is now like the third time suspense is being rendered is it awaiting a promise no because that promise has been resolved this time so it hits our asynchronous code which it's using the cache to basically find out have I already made this request before is it the first render so the first time it was yes the first render but this time no it's not the first render and the reason it's we know it's not is because of this little if statement here so we've created the key read that key from our cache which gave us back the value and this time the value has a status of resolved because we changed it to that down here when our fetch promise was resolved so this time we've read it we no longer have to create this fetch request because we already have the result we can simply return it so now when this is rendered it will simply receive the data back which were displaying as a string if I'd json.stringify right here so that's all of this and then we could quickly render it out so why don't we just change this to a ul which inside will do data data because our data has an attribute called data which is an array of all of the different routes so data dated up map so we'll call this the route and it will render an Li and because we're mapping we need a key right so our key can be probe dot ID and our value in here can be a wrote dot attributes dot we'll go with a long name like that okay so if i refresh loading for a split second it's a quick API so it's not providing a huge benefit right here but then when it gets the data it rear Enders and shows out the final result so I just want to go into a little bit more detail about sort of this loop how it's being rendered in multiple times so if I come in here and I've had a console dot log of the value that we're getting back this will allow us to see that this codes being called twice the first time it will be called and because the key doesn't exist in the cache we're gonna see this value but the second time it's called we're going to see that it's been resolved and that we've got our data back that we got from our fetch so if I inspect here so we've got our to consult logs here so the first time it's new is null the second time it's resolved and the data is all of this data in here I actually think because I didn't do things immutably it it modified the same object as before so if we wanted to now just for the heck of it why don't we do this blood Emmer to our project so we can do things immutably all right we'll start back up so we'll come to our code and we'll Court produce from Emmer and if you've never seen Emmer before it basically lets you immutably change data in JavaScript using sort of a mutable style so what we'll do here is we will say that we're setting the key to so instead of just putting in value we will call our produce function which will receive the first thing is the data we're going to be immutably changing so that's the value that was loaded for my cash and then what we're going to be doing is of a new copy of that is our draft data into this arrow function here and here we can just simply say draft status is resolved and draft data is data so now I no longer need these okay so just to reiterate and regurgitate what happened there we're setting our cache the key to be a new value and we're using produced to basically create a copy and change the previous one that was in the cache prior so we're using produce which receives that as its first argument then it passes to this arrow function here a copy that we're calling draft which we can modify an Emer basically keeps track of all the changes we're making to it and it returns the new immutable changed value of that that we produced hence why it's called produce so now if we come up and we reload this now when I inspect data is actually null its and then the second time when it's resolved we're getting our actual data here cool so that little aside had nothing to do with creating custom hooks or suspense or anything but Emer is my favorite tool for dealing with immutable data so if we just review this from the top down we start in our home component and we wrap suspense around our component that will execute asynchronous code and when I say synchronous it's basically something that throws a promise and eventually resolves that promise so if it hasn't been resolved yet it shows this loading data component if we go into Boston routes this is a functional component that has our custom hook here and this custom hook passes in the URL along with any fetch options that we want to pass on to the actual fetch call we come in here it takes the URL and the fetch options and it builds a cache key and then it looks up using that cache key the cache and because the first time it's called it will not have a value we initialize it to something if it did find something and that something has already been resolved great we have the data we can return it if it's not resolved meaning it's this guy right here status new we perform our fetch passing in URL fetch options and then on the first promise we are going to convert the response into JSON we have our promise back and the reason we put this into a variable instead of just doing a dot then right away is so that we could later throw with this promise but down here we say okay so when this promise resolves take this data that came back from response to JSON and update the cache that's what we need to do so using the cache key we're using immer to produce a copy of this previous value updating its status to resolved changing its data to be whatever came back from me API call and because suspense detects now that this promise here that we through that was caught up here by suspense has been resolved it triggers a rerender which comes into Boston routes calls our use fetches Ben's code this time it finds the value it's resolved that values returned so we can now finally render out the data that we got back from the call so you may be thinking this was a heck of a lot of work to just make a fetch call and the reason it was a lot of work is because we created this hook but now we could reuse this hook whenever we wanted and it would only be this much code along with a suspense wrapper to get asynchronous code that handles the loading state with us having to manage our own as loading true or false if loading is true and all that stuff that you've typically had to do cool that's it in the next video in this little series I'm going to be creating another hook that will basically end up looking very similar to this one except instead of making a fetch call we're going to be doing a CPU intensive function call that will execute some code on another worker in the browser and when that other worker is finished loading it will render the result oh but we're going to be using our hook and suspence to to help with this cool hope you enjoyed it thanks bye
Info
Channel: Leigh Halliday
Views: 4,687
Rating: 4.9749999 out of 5
Keywords: react, hooks, suspense, fetch, asynchronous
Id: PtoQpVOQ5Ew
Channel Id: undefined
Length: 27min 39sec (1659 seconds)
Published: Tue Jan 22 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.