React-Query Tutorial - OUTDATED - LINK TO THE NEW VERSION IN THE DESCRIPTION

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey everyone today i'm going to show you how to use react query this is a library that allows you to handle the kind of global state that should be synchronized with the backend in your application in this video i'm going to show you how to build a simple to-do application where you can create new to-do's test test add to do we can load more it works with pagination so initially it loads only two items and then we can load more and then load more we can uncheck the items and as you can see it happens immediately even though we have to update the data on the backend and we do it using optimistic updates i'm going to show you in the end of this tutorial how to do this as well and if you click delete for example it doesn't have optimistic update so it takes some time when you delete the item and then it queries the backend and then it re-fetches the items for this example i use airtable as a backend so we can actually add the to-do items here new to-do item and we can probably remove a bunch of records a bunch of records from it and then if you go to the application it will automatically refresh the data because direct query automatically refreshes the data when you refocus that tab with your application which is quite handy so let's start by creating a new react application i'm going to generate a new app using npx create react app react query tutorial we'll have to wait a little bit after it's done we go to the application folder react query tutorial open the code we'll also need to install react query yarn add react query and i'm also going to copy the dot n file where restore my air table key cp rq to do dot m to the root of our project let's close this panel go to src and first we'll remove all the layout now to fetch the data inside of your react component you would probably use use effect and a bunch of states to hold their status of this fetching process like is loading flag is error and maybe an error object and the data that you're fetching from the backend with react query you don't need to do all that you can just use the use query hook to get the data error is loading is error flags and the error object and then you call use query and then you need to pass a unique identifier of this query in our case it's going to be to-do's and then you pass the fetching function the only requirement for this function is that it returns something then able like a promise so it's very good for testing because you can easily substitute the real fetching function with a fake one and also if you want you can later transition to graphql if you open react query documentation you will see that they even have an example using graphql request that can create the request a graphql server for you but let's get back to the code we need to define a function that will fetch the to loose from the airtable i usually create a new module called api.js that holds all their fetching functions for my application this way it's easier to refactor later and i'm not bound to specific fetching mechanism here i first need to define the data specific to my error table i'm going to copy it from the previous project and paste it here it contains the base the table the view and the api url we're going to use it to create the fetching urls we'll also need a constant to hold the authentication header i generate it as an object as an object literal with the authorization header which is bearer plus the information from the environment that i take from the dot n in create react up generated applications if you want to use environment variables you need to use the react app prefix before their name so in my case it's react app airtable token and it will be loaded to the environment automatically when we build our application now let's create a function called fetch todos and export it export const fetch to those equals we need to get a key we're not going to use it in our function but react query will send it anyway and then we define a function that will fetch our data first we need to define the url i'm going to use a template stream and combine the api url with the base name the name of my database the table name and also i'm going to pass a query parameter with the view id now we return fetch i pass in the url here it's a basic get request so we don't need to send anything else except for the authentication headers i pass an object with the field headers and here i pass another object literal with the structured auth header object after we got the data we call then response and then we're gonna pass parse it as a json res json and that's it we can go back to app.js pass in the fetch to lose now we need to process the loading state if is loading then return a paragraph that says loading then we handle the error state if is error we return a paragraph with the error value and we should use return here as well and then we can render the layout so i'm gonna render an unordered list ul where i'm gonna map through data records and for each record i'm gonna do the following i'm gonna return a list item with the key that will be record id and text record fields text now let's open our application yarn start oh and i've defined the error object twice so let's go back remove this error from here and go back to the browser now we can see the to-do items let's go to the application styles and position them in the center of the screen go to index.css first we are going to add display flex to the body align items center justify content center we also need to specify height for height 100 for body and html html body height 100 percent let's open the app now everything is centered next thing we're going to do we're going to add the pagination to our app let's go back and to do this we're going to need to use another hook instead of use query we're going to use infinite query so let's remove this use query use infinite query and it has a little bit different api the idea is that when you use the pagination then with every response you also get a cursor that you can use to fetch more data for your application airtable supports this functionality and with each response if you specify the amounts of item the amount of items per view that is less than total amount of items that you have it will also send a cursor variable so let's do it in the api first we'll need to define a query parameter called page size page size equals and here i'm going to use another placeholder and for now i'm going to use two now we'll also have to process the cursor variable or attribute in our function function cursor and then we check if we have cursor passed in our fetch to dos function then we also append the offset to our url url equals url plus template string and offset equals cursor and then otherwise the function stays the same then we go to app.js and now we should also pass an options object with the field get fetch more this function will get this cursor value from the data and also it will tell you if you can actually fetch more for example let's use the last group name for the argument for this function it's going to just check the value of this last group basically the last fetching response check if it has the offset field and then just return it so if the last last fetch response had the offset field it will use its value or if it's falsy for example it's undefined then the can fetch more value that you can get from this hook can fetch more will be false so let's add a button to our layout so that will fetch more data when we click on it let's add a div that will hold the button a button itself and on click it should call a function where we'll call the fetch more function that we can also get from the use infinite query fetch more now if we cannot fetch more let's disable this button so the attribute disabled should be not can fetch more or if we are already fetching more data is fetching more and we also get this flag from the use infinite query is fetching more let's format the document and now let's specify the text of this button here depending on the status for example if is fetching more then we say loading more otherwise if we can fetch more then we say then we say load more otherwise if we cannot fish more then we say nothing to load and then we format the document let's open the app and here as you can see the data that we are getting is now different we are getting an array of groups so every time you fetch more instead of appending it to the previous request you just have a growing array of request results let's go back and for now let's just lock the data and see how should we handle it console.log data let's open the console and here is our fetch data so it's an array and then inside of it we have their responses let's go back remove this log so now we'll need to have another loop so we're gonna map through the data data map and for each group and its index we're going to do the following we're going to have a react fragment with key equals i and then inside of it we're going to map through the records of the group let's format the document and go back to the browser okay now we have two items two first items we can click load more and load more items from the air table and if there is nothing to load we see this as a text on the bottom nothing to load next we want to be able to add new items go back to the code and create a new component by the way we can remove this app test.js it's not working anyway create new component called add to do dot js import react from react and then export and define a component add to do it's a functional component and for now let's just return a an empty fragment so react query api is very similar to react apollo i would say if you open the react query documentation you will see that it has the same concept of queries to fetch the data and mutations to change the data to update the data on the backend the same way as it works in graphql but instead of graphql server you're talking to rest api so here we'll need to create our first mutation but before that let's create a layout here we want to have a horizontal line separating this component from the rest from the list of the items you want to have an input i'm going to use an uncontrolled input so i'm going to use a ref to get the value from it and i'm going to create the ref to hold it input ref equals use ref and then i pass it as a ref to the input input ref we want to close the input tag and we want to have a button that's going to say add to do and on click we want to call the on add function const on add and here we'll call our mutation in this one function but before we need to define this mutation const mutate add you can use any name because it's an array item that you get by destructuring the array of arguments from the use mutation query use mutation and here we need to pass only the mutating function create to do let's define this create to do in the api.js module export const create to do equals we need to know the fields like text and checked and checked in our case it's going to be only one field but it's better to just send all the fields that we want to from the client so we'll have one argument fields there's going to be an object containing all the fields of the newly created to-do item and inside of it we are gonna construct the url it's gonna be a template string again that we get by using api url slash database name slash table name and then we return fetch to this url and then we pass an options object first we need to define the method when we create a new item we have to use the post method method post we want to send the headers first one is authentication header and then we also need to specify the content type content type application json and then we need to pass the body of the request where we send all the fields of our to-do item that we want to create so the field is body and the value is json stringify the object with the field called fields that will have the value of this fields argument format the document go back to add to do let's import the create to do from the api and then when we call on add we want to call them mutate function with the value text input ref current value and we also want to reset the input value we do it using input ref current value equals empty stream let's go to app.js and add our add to do component save the file go back to the browser and let's try adding new items blah blah add to do what more if we open the table you will see that our item is created but we don't see it in the updated application now when we reloaded the page it was added to the app so how can we force re-fetch the information when we mutate the data on the backend you do it using the query cache object let's go back to the code open add to do get the query cache const query cache equals use query cache and now we want to add an options object to the muse mutation call and pass on success function so when the mutation will be performed we're gonna call a function that will call query cache invalidate queries and in our case the query's name is todos so we want to re-fetch the to-do's query in the app.js so we invalidate to-do's and it's going to be re-fetched let's open the browser again i'm gonna go to to do app and remove this empty record from here let's go to the app reload the page and try adding a new item like one two three four five i had to do a lot more and it should be somewhere in the list let's try adding another one with some random numbers had to do and it was automatically fetched even before i clicked the load more button now we want to edit the items for example to be able to uncheck the checkbox and to be able to remove the edit item from the list let's go back to the code and we'll need to create a component to represent the to-do item let's create a new file and call it to do item dot js import track from react and export a component called to-do item that should get id text and completed status from the props now inside of this component we return some layout it will actually be a list item inside of it there will be a span that will say text we'll also need a checkbox it will be an input type checkbox on change should equal on check we'll define this function a bit later and checked status should be in double exclamation mark to convert it to boolean and complete it let's format the document and add a button that will allow us to remove this item it will say delete and on click it should call the remove function now to be able to remove the item we'll need to create a mutation first we define the remove function it's an arrow function and then we define a mutation mutate delete equals use mutation we'll use a delete to do function that will define in the api module and we need to pass some options for example we need to re-fetch the to-do's when we successfully delete the item so onsuccess equals a function that should clean up the query cache let's get it using the use query cache hook cons query cache equals use query cache and then we call query cache invalidate queries called todos now we can call the mutate delete inside of the remove function we call mutate delete with id and let's go to api.js and define the deletion function export const delete to do it should receive the id of the item and then we construct the url a template stream that will get the api url slash base name slash table name slash id and now we call the delete method return fetch url method delete headers and we need to path past the authentication header the structure the auth header object and that should be it let's make sure that we import this epi method delete to do now let's go to app.js and instead of the list item let's use the turo item here let's destructure the record we know that it has id and fields then we can pass the key id the id prop that should also be id because we need to have the you know the id of the item to be able to remove it here we get it from the props inside of the to-do item and then we can actually pass all that all other fields we do it like this the structure fields and pass whatever items we have in the fields argument now we can go back to the browser to do item on check is not defined okay i forgot to refine this method let's define const on check it's a function for now it's not going to do anything okay can we remove an item delete and nothing happens if i open the terminal i see that i made a typo with on click let's fix it the l should be small let's go back close the console delete the first item boom it works now let's add the check on check functionality let's define another mutation const mutate check equals use mutation we'll define the update to do in the api and we need to pass an options object with the onsuccess field here we'll do the same just like we did for mutate delete we'll call query cache validate queries and we needed to re-fetch the toulouse query now inside of the uncheck function we call mutate check and we pass an object with id and fields were completed equals event target we need to get the event from the as an argument to the function event target checked we need to wrap it into curly brackets and format the document now let's go to api.js and define this function that will update the items for us export const update to do equals a function we need to know the id and fields now we construct the url url equals template string with the api url slash base slash table slash id and then we return fetch url and an options object the method should be patch because we update the specific record we need to specify the headers of course we'll need to pass the authorization header but also we need to pass the content type because we're sending the data content type equals application json and then we need to define the body body is json stringify and we stringify the fields just like we did when we created a new item it should be fields and now everything should be fine let's go back to the to-do item and make sure that we import the update to do function let's open the browser check the blabla to do item and it works but between the click like now click and the update there is a significant delay that probably would be annoying to our users so what we can do here is we can use optimistic updates so we can update the state the status on the client inside of the query like manually change the cache cached value and then it will still automatically perform the request of the server and then get the actual data and invalidate the previous change if it was wrong and there was some error so how can you do this let's go back to the code and we'll have to update the mutate check function instead of using the unsuccess callback we'll use three other callbacks first one is on mutate so this callback will be called when we trigger the mutation but before the data is sent to the backend it's going to be a function that will receive the new to-do item the updated value inside of it we need to conceal all the other queries related to it query cache conceal queries to rules so if we are fetching anything we need to just conceal it immediately and then we need to get the previous state of this query const previous query equals uh here's a typo query cache get query data to ruse so we get the current state and then we need to update it we do it by calling query cache set query data for todos and we pass in the function with old query and then inside of it we do the following we return an old query where we map over the groups you remember that we have groups of responses there and for each group we return an object with this group value but for the records we map through the group records and for each record we do the following if record id equals new to do id so if it's the item that we are trying to update then we return a new object created from the old record record plus the fields from the record fields merged with the new to do fields so we merge the previous fields of the to-do item with the new one so we can update fields one by one otherwise if it doesn't match then we just return an old record without updating it after we do all that we need to specify the cleanup function the function that will be called if we need to revert the changes return a function query cache set query data to lose and then we just set it to the previous query query so we basically conceal all the changes that we just made if something goes wrong then we need to specify the on error callback and here there will be an function that receives the error object the new to-do item the item that we're trying to update and the rollback function and we just call the rollback then we need to specify the unsettled callback when everything went as expected and here we'll just reset the to-do's query cache new to-do and here we query cache invalidate queries to lose and that's it now let's go to the browser and try to check and uncheck the checkboxes as you can see everything works fast now because we are manually updating the data in the cache and if we reload the page the data will still be valid updated let's see if we can still remove the items we can indeed remove them and actually react query can do much much more you can conceal the queries you can prefetch the data if you need it you can disable or pause queries it can refresh the data on window focus you can use it with the server side rendering and a lot more so i encourage you to just read the documentation and it's super cool it supports typescript and you can even use it with graphql and here is the example using the graphql request as i said in the beginning so thank you for watching let me know in the comments what else would you like to know about react query maybe you want to know how to test the applications where you have it or maybe you want to have a more comprehensive example of it so if you're interested just let me know in the comments or join my discrete server the link for it will be in the description thank you for watching and see you next time
Info
Channel: Maksim Ivanov
Views: 22,695
Rating: undefined out of 5
Keywords: react query, react query tutorial, react
Id: GE-waX4jmdA
Channel Id: undefined
Length: 29min 29sec (1769 seconds)
Published: Sun Oct 18 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.