Custom React Hook for Fetching Data (useQuery)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey what's going on guys welcome back to another video today I'm going to show you how to create a custom hook for fetching data from an API and displaying it on the screen I'm also going to go over how you can run side effects when the queries fetched like unsuccess or on error so be sure to stick around to the end of the video to see those I also wanted to mention that only three percent of my viewers are subscribed so if you've been enjoying the content be sure to hit that subscribe button and turn on notifications so you don't miss a video and without further Ado let's get into the code [Music] foreign so I just have a basic app here which as a chocolate provider and then a container for the whole app just for styling purposes and I have one route to find and that's in this post component so if I open this up you'll see that I just have some State here that manages the posts coming in the loading and error States and right when the component renders it's going to run this function which sets the loading state to true fetches this API and gets back a response and then sets our post to the response data if there's an error of catching it and then setting the error state to true and then finally I'm setting the loading state to false then down in the component I have the heading and if it's loading I'll output the circular progress which is like a loading spinner if there's an error I just output an error message here and if the post exists meaning they refresh and set in the state then I'm just outputting a stack here and then mapping through each post getting the ID title and body and then displaying those in the card I also have this button that will navigate the user to slash post slash ID slash edit and we'll be creating that route later so this is a pretty simple component and when we go back to the page you can see I just refresh it fetches the data and then sets the cards now the way that we currently fetch these posts is not good for a number of reasons one is that it's not reusable at all so if we ever needed to fetch these posts in other parts of the app then we're basically just going to take this whole chunk of code and copy and paste it into every spot that we need it and if something were to change like the path of the endpoint for example we'd have to go back and find every spot that we use this and then change that so it's incredibly inefficient it's also super verbose all of this logic takes up quite a bit of room and if we had other pieces of state or any functions we defined in the component this file will start to get very long and hard to maintain so instead what we want to do is create a hook that will wrap all this logic into its own component and then we just want to be able to call the hook at the top level like use Query and then this hook will return the data the loading and error States and any extra info we want to return so this will be a much cleaner way to fetch the data and will be able to reuse this hook everywhere that we need to and if we ever need to change something about the logic or the endpoint we would just change it in the one file where all this logic lives so let's go ahead and create that file we'll go into Hooks and call it use Query and then I'm just going to copy and paste the logic we had from before so let's come down here and create a constant called use Query and this is going to be a function and inside is where we're going to Define our state and the fetch function so let's begin by creating our query state and our initial state is just going to be an object instead of the three pieces of state that we had before which are these three things posts loading and error we'll just get rid of that and we're going to store all that state in an object and we don't need this navigate here and I'm just going to paste in these properties so data is loading as success is error and then the error message that occurred now let's come up here and grab the state that is returned so we'll do state and set state and then we need to replace all of these old functions with the new set State function instead of passing a value for the new state we need to pass a function grab the previous state and return a new object and we want to spread that previous state across and then we want to override the is loading property and we'll set that to true and then we need to do this for all the other functions so if it was successful then we'll pass in this function and then we want to set the data property we'll set that to response.data we want to set is loading to false we want to set his error to false as well if it was previously true and then also the error to an empty string and then we'll pretty much do the same thing for the error Handler here except we'll set data to null this voting can stay false is error will now be true and the error can be the error.message or if that doesn't exist we'll just add a default value like failed to fetch and then we could get rid of this finally block since we're setting the voting States here now we just need to come down to the end of the file and return the state so now this state which is this object here will be returned from the use Query hook so now we can just call this in our post component and we'll get access to all these properties and this component will take care of all this Logic for setting the state fetching the data and updating the state we just need a come down and Export it before we can use it and then we'll come into posts and destructure the properties it returns so we'll call use Query foreign and we'll just do is error and we can rename data to something more specific like posts and then we need to change out these names for the new properties so it's loading and as error and if we hit save and just make sure we're importing everything we need in this file to use effect and axios and then we also want to make sure we return the state outside of this use effect because this was nested inside this function so we just want to make sure it's at the top level at the component so if we hit save now and load our app we should see that those posts come back and it's working just as it did before so although this works this is still not very good because it's not really reusable we have this URL hard coded and although we could call use Query and get the posts everywhere on our app what if we wanted to get users or another piece of data to improve this we could take in a URL here as a parameter and then pass that in here and make it board dynamic in that way but still that's very repetitive because if we had the same post URL we would have to pass that in every single time in every place that we use this instead to get ultimate reusability we're going to just pass in a function and that function is going to take care of fetching data from the API and returning the response and then all we need to do is call usequery and pass in an API getter function and then this hook will take that function run it in the use effect and then update this state accordingly let's create that API getter function if you open up the api.js you'll see that I'm initializing an axios client with this base URL and now I could use this API variable to call the different methods for maxios like get post delete and it will have this URL set already so we would just have to pass in the path that we need let's come down and declare a constant called get posts it's going to be equal to a function that is going to call Api dot get we just need attack on slash posts and then we're going to grab the promise here and get the response and then we'll just return the response.data so just like we were before and we need to add a const here and now get posts is going to be a function that runs API dot get and then just Returns the response data and we can just destructure data here and return that so now we can go ahead and pass this function into use Query so we'll come in here paste it and import it and now in use Query we need to grab the function that was passed so we'll just call it FN and down in the use effect instead of doing this axios call we want to just call the function that was passed and before we call it we want to make sure that there was actually a function that was passed so we'll just do a quick check here so if there's no function just return out so then when we get to here we know that function was passed and then we're getting the response as a parameter but we no longer need to do response.data anymore because in the function that we're creating we're destructuring the data and returning it so if it makes it any easier to see I'll just split the tab and show you what's going on so when we call this function here we run get posts and get posts will go to the API fetch it at slash posts and then get the response destructure the data and return the data so then this return value is the parameter here so we're waiting for this promise to resolve and then we call Dot then get this response which is this data that we returned and then we're just setting the data state so let's just change this to data and we could remove this so now we never have to worry about accessing the data prop or any other custom property that an API May return all we have to do is make sure that from these functions we return the exact data that we want to be set in this data property so now if we hit save we should see the post coming up and it still works just to clean this up a bit I'm going to take all of this in the function and just to clear it above so we'll do const run query is equal to this function and we'll keep the logic the same and then we'll just pass in run query here so it just cleans it up a little bit and it's easier to see and this should still work as before and we could test the error handling by just trying to access a post that doesn't exist and you can see it says failed to fetch posts our error handling is working now the next thing I want to do is return a refetch function so if this data ever becomes stale or we want to give the user the ability to just refetch it they should be able to just click a button we can just come down here and return an object and we'll spread the state out so they can access the same properties but then we'll also return a refetch function and this refetch is going to be equal to run query so when the user calls refetch from whatever component they're in then it's just going to go ahead and run this query which will again check for the function and do all the same logic so now we should be able to come back in a post and destructure refetch and now we could create a button at the top that will call that function let's come up to this loading State and we'll turn this into a ternary so we'll check if is loading's true we'll output this otherwise we want to Output a button and in here we'll just call it refetch and then we'll add an on click Handler and set it equal to refetch this is the function that was returned from use Query and then I'm just going to paste in some styles and if we hit save we should be able to see that button and once we click it it'll refetch now let's work on this edit endpoint because when we click edit takes us to slash post slash ID slash edit but we don't have a component yet that handles that route so let's come into the app and create another route and we'll do slash posts slash ID and then slash edit and then we'll call this edit post and we need to create this file so we'll go into components and call this edit post and let's start building out the logic here the way that this is going to work is when we go to edit we need to grab this ID that was passed and then fetch the post by ID and set it in local state since we're going to be editing the post we want to set that post to State instead of just having it in a query like this because we can't update this so let's start off by grabbing the ID and we get that from use params and that comes from react router and it returns an object with all the dynamic parameters we defined so since we called this ID we can destructure it here and now let's create some state for our post we'll do const and set post and by default it's just going to be an empty object now let's bring in our use Query hook and again we need to pass in a function as a parameter and we need to First create that function so we'll just copy this and do get post by ID it's going to take in an ID and then we'll turn these into template strings so that we can pass Dynamic values so we'll do slash post slash ID and by doing this dollar sign curly braces we could pass in any variable into the string so now we can go ahead grab this and pass it into use Query since this function takes a parameter we can't just call it like this and pass in whatever ID but we could just turn this into a little wrapper function so we'll just do an arrow function here and call get post by D and then we'll pass in the ID that we got from use params and here we get the same properties returned so I'll just copy these over this is no longer going to be posts but it's going to be initial post so this will be the initial post that comes back from the API and since this is an edit route we want to be able to update the post so this will be the initial post but then the post that we're going to be updating is going to be this post in the local state so we need to figure out a way to set this post in local state once this query finishes as I mentioned earlier we can run side effects for one queries finish and what we want to do is once this is done we want to run an on success function that will take the results of the query and then run setpost to then set it in local state and then we can use it in our component so let's update this hook to now take those side effect functions now we could take in a second parameter which will be a config object and here is where the user can Define certain side effects like on success or on error and if they don't pass anything in we can just default it to a default config let's just create that up here we'll do default config it's going to be an object and we'll do on success and we'll just set it to an arrow function that does nothing and we'll actually store this in a constant above because we're going to reuse that we'll call it no op now we could just pass this variable in here and we'll Define an on-air Handler as well so now the default config if the user doesn't pass anything in as the second parameter it'll just be this object that has these properties and runs a function that does nothing so it's not going to have any side effects now we could come down here and in our query if it was successful then we set our state and right after we'll just run the on success and we need to destructure this first from the config so we could just do const and take these two properties and we get that from the config that was passed in and then for any errors that were caught we'll set the state again and then just call on error and we also want to pass in the data that was returned from the API to both of these functions and now since we're passing it in and calling on success we can come back here and Define an on success Handler and this will be a function and again we get the data which is what we return here we pass that in as a parameter and we just want to run a function that calls set post to the data that was sent and this needs to be a comma and now when the post loads in it should run this function and set that post and I'll just log the post real quick and we could see if it works let's just import edit post and this actually has to be a jsx file since I'm using V and we'll change use Query as well now if we go back to our app we should see that post being fetched and there it is now let's focus on building out this edit post component first we're going to Output a box here with some Styles and we'll add a button here that is going to have an on click handware that will just navigate back to the post page and we'll add a card here and inside we'll render a box and we'll check the voting State and output some text we'll also get the error State and then we want to check if the query was successful and if it was we want to Output this stack here and in the stack we'll display the title of the post and then an input to update it and this value is going to be coming from the title that's stored in our state and we also want to grab the body and this will be a text area that is also hooked up to the state and it'll just call this handle change function which will take care of updating the state and the necessary properties so now let's create this handle change function to update our state let's come up here and do const handle change it's going to be equal to a function and it's going to take in the event and we're just going to destructure the Target and this function is just going to call setpost and in here we're going to pass in a function again so we'll get the previous state and then we'll return a new object we'll spread the previous state out and then the property we want to override is the target name and if you look back at our inputs you could see for the title input we put the name as title and for the body one we have body so we have access to this name attribute on the target so we could open up square brackets and do Target dot name and that'll be the property and then the value we want to set it is to the Target dot value and now on every onchange event we're going to destructure the Target and update the state based on the name of the input that triggered the event and we'll set it to the value and then the value of these are body and title and we need to get those from our state so we can do const title and body and that will come from post and then in here instead of getting is error we want to just grab the error itself because that's what we're displaying down here so that would be the error message and in order to use this navigate function we need to call the use navigate hook which comes from react router so we can do const navigate equals use navigate and if we save this we should be able to see that post and be able to edit it and his Success is Not defined so we just need to pull that from here and we don't see our card here and that's because we need to set is success to True here and we also need to add it to this error and where and we'll set it to false down here and now if we hit save we should be able to see that card come in and once it's loaded in we could update it through the local state and that's all working as expected that's it for this video thank you guys so much for watching leave a like if you enjoyed And subscribe if you haven't already it would help you out a ton let me know in the comments if you have any feedback or what you guys want to see next and I'll see you in the next one [Music]
Info
Channel: Nikita Dev
Views: 5,375
Rating: undefined out of 5
Keywords: javascript, react, react js, react tutorial, react hooks, react custom hook, react query, useQuery, react hooks tutorial
Id: si3AZsAkZlE
Channel Id: undefined
Length: 21min 25sec (1285 seconds)
Published: Sun Feb 05 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.