URL Params as State in React (Complete Tutorial)

18.1k views4855 WordsCopy TextShare
Cosden Solutions
Try Brilliant Free for 30 days 20% Off Premium Membership → https://brilliant.org/CosdenSolutions ...
Video Transcript:
In this video, I'm going to show you how to  correctly use the URL query parameters as state in React, because sometimes you want your state to be  persistent. You want your state to persist perhaps when you're reloading the page, or even if you're  sending the URL to somebody else for them to see, you want to also send along the state with it.  Now, before we get into that, there's two things that I want to mention.
And the first one is that  if you want to learn how to build a real project with React, a project that's big and complex and  has a lot of moving parts, definitely check out Project React in the description. I promise you,  you will not regret it. The second thing is that I've also recently launched a newsletter that's  all about React.
It's called Import React. And honestly, I love the name, it's completely free.  And you get it delivered to your inbox every single week, you're also going to be able to find  it in the description.
Cool. With those things being mentioned, let's now look and see how we can  actually put things and use the URL as state in React. Alright, cool, let's begin.
So this is the  application that we have and that we're going to be working with in this video. This is actually  the same application that I did in the video where I showed how to create a custom filter  component, which is this component over here, the search the category and the max price, right?  So we can actually search or we can do something like product, this is going to work and it's  going to search.
But now the problem is if I refresh this, we lost that state, this text input  here has no value, we lost that state. Even worse, if we tried to send this URL to somebody  and we wanted to also send our search query, this will be lost because nothing is stored in the  URL. So in this video, we're going to fix this is the code that's running this application, I will  briefly go over it because I did build this in a previous video.
But essentially, we have the app  component over here. And we have a couple of state variables here, we have one state for the search  one for the category and one for the max price, right? Simple enough.
Then we're taking those  values, and we're passing all of them to react query, we're passing them here in the query key  so that we have our caching working as we expect. And we're also passing them here to the fetch  products function. And the fetch products function is going to take those values and return to us the  products that actually match.
And then over here, we have the JSX that's actually running this  entire application, it is pretty simple. And we have the product list filters component, which  takes in an onChange function, it calls it with the filters. And here, we're just taking those  filters and putting them in the state of this app component.
And then the product list filters  component has the same state variables over here, it receives the onChange function. And we  basically have a use effect over here, whenever any of these values changes, we're actually  changing and calling the onChange function with these values. And then we have some JSX over here  that just has everything that we need to actually render the UI for this filter component.
So now  what we're going to do is we're going to take this code, refactor it, put all of this code inside of  the URL instead and make this hopefully a little bit better. But before we do that, I just want to  take a very quick moment to talk about brilliant, which is the sponsor of this week's video. So  brilliant, just like what we're doing here in this video is a place where you learn by doing  you have 1000s of interactive lessons in various topics such as math, data analytics, AI, and  more importantly, for us programming brilliant has a platform that is designed to be uniquely  effective.
Each lesson that you have on there, you get to solve using a hands on approach that  lets you play and get a feel for all the different concepts, which actually has been proven to be  a method six times more effective than watching traditional lecture videos. Plus, all of the  content on brilliant is crafted by an award winning team of researchers and professionals  from places such as MIT, Microsoft, Google, and more brilliant actually helps you build your  critical thinking skills through problem solving instead of memorizing. So while you're building  real knowledge on specific topics, you're also learning how to become a better thinker.
And you  can do all of that on brilliant with literally just a couple of minutes a day, you have fun  lessons that you can do whenever you have the time. It's the opposite of mindless scrolling. And  it actually builds a great habit.
And if you want a recommendation for a great course, check out  thinking in code, which follows what we're doing in this video and teaches you solid foundations  for computer science, which is going to directly help you as a react developer. To try everything  that brilliant has to offer for free for a full 30 days, visit brilliant. org slash cause and  solutions or click the link in the description.
You're also going to get 20% off an annual premium  subscription. Thank you once again to brilliant for sponsoring this video. And now we can get back  and start refactoring and start using the URL as state and react.
So to be able to do this in react  and to put everything in the URL efficiently and have this work really beautifully, we're going to  need to use a library that is all about routing. In this example, we're using if I just open  up the main. tsx, we are using react water, which is a standard, it's a very popular library  for routing in react applications.
So here we're creating our router, and then we're doing all  of the traditional setup codes to actually put everything through a router. And this is going to  give us access to some handy hooks that allows us to modify the URL, which we can use to actually  put things in there. Now the first thing that we're going to want to do is we're going to want  to take all of this state and have this come from the URL instead of just be simple state.
And the  second thing that we want to do is we actually want to get rid of duplicate state. Because we  have this state here, we have the search category and max price. But we also have it here in the  app component, literally the same state variables.
And we have this unnecessary on change function,  which honestly, we don't need to this code isn't as efficient, even though I wrote it, right,  that's okay. That's part of being a developer, we're going to now improve this and make this a  little bit better. And the best way to do that is actually to create a custom hook and then put all  of these state variables in here and then create a function to update them and then use that hook use  that custom hook in both places or in any place that we need to have the same filters for the  products.
So let's do that. So I'm going to come here in the sidebar, I'm going to go inside of my  hooks folder, and I'm going to create a new file. And we're going to call this one use product  filters dot ts.
In here, I'm going to define a function export function use product filters.  And we have the function over here. Now in here, because we are using React router, right, that's  the reason why we're using this, we're going to use one hook that React router provides us, which  is going to allow us to easily access the search parameters of the URL.
And that is going to be  the use search params hook. So we're going to do const search arms, set search arms, and that is  going to be equal to use search params. And we're going to import this from React router.
This has  a very similar syntax to use effect, this is the actual value of the search parameters. And this  is the function that we can use to actually set any search parameter value, right, it's pretty  simple, it's straightforward. And we get access to this because we are using React router, then  what we want to do is we want to get and create variables for each of our state properties, right. 
So that is going to be the search the category and the max price. So we're going to come here and  make a new line. And we're going to have those come from the search parameters.
So we're going  to do const search equals search params dot get, which is a function that we can call, then in  here, we're going to pass search like this, right, this is going to be our key, which is  going to be stored in the URL. And then what we want to do is we actually want to get the  correct value, the correct type of this search, we're going to alias this as the type that  we have here product filters, this comes from our products on API, these filters over here, I  want to use those as our state variables to get the same types and the same functionality that we  had previously. So what we can do is we can do as product filters and import them from API slash  product.
And I believe here, we're going to have to pass search like this. There we go, we have our  search done. After that, we're going to want to do exactly the same thing for the category.
So we're  going to do const category equals search params dot get category. And we're also going to alias  this as product filters. And we're going to pass your category like so the actual type of product  filters category is this string literal over here, it can be first, second or third.
And for the  search, it is going to be a string. And all of these are optional. So actually, this works  because the search params dot get can also return a null value, which fits with the optional  type that we have here.
So that's great. And then we just need to do our max price. So we're  going to do const max price equals search params dot get max price.
And here, we're going  to have to do is because max price is a number, but inside of the URL, everything is a string,  we're going to have to actually convert this to an actual number. And to do that, we need to first  check if we actually have search parameters max price. So I'm going to do question mark over  here.
And then if we have it, we're going to do parse int. And then we're going to do search  params dot get and then max price. And then here, otherwise, we're going to return on defined  to match with our actual state.
And here, yes, we need to alias this as a string. I know it's not  really efficient. But because it doesn't know that we actually have the value over here, I'm just  going to alias this as a string, we could also do a little bit more fancy code to actually make this  work.
But for the purposes of this throw video, this is perfectly fine. So now we have search, we  have category, we have max price. And these are the same values that we had previously in our  actual state variables.
But now they're coming directly from the URL. So we're no longer using  state from react, we're just using the search parameters over here from your search params from  react router, which is coming from the URL. So that's great.
We're one step closer to refactoring  this component. Then once we have our values, we actually need to also use set search parameters,  which means that we need a function to be able to update these values inside of the URL. And for  that, we just need one function that accepts all of these properties.
And based off of those, we'll  actually just set them and update them in the state. Let's do it. So I'm going to come here and  I'm going to create const set filters.
And that is going to be and here I'm going to do something  that I always do. And this is a good pattern to do in your react applications, I'm going to  use use callback like this. And I'm going to do this as a function that uses use callback with  no dependency array.
The reason I'm doing this is because we're going to be returning this function  from our actual hook to be used in any component in the productless filters component in the app  component, or anywhere else. And the thing is, whenever you're creating a custom hook, and you  have a function here that you're returning, you want to always put use callback because you cannot  control where that function is being used in other components. And if you have another component, for  example, the productless filters wants to use this set filters function and put it inside of a use  effect, which is a valid use case that they might have, they would have to wrap it inside of use  callback.
So we don't want to have them do that, right, we want to do it instead ourselves as the  owners of the custom hook. Right, and do that extra step for whoever else is going to be using it.  This is also what third party libraries do.
So remember to keep it simple. If you're creating a  function inside of a custom hook to be reusable, always wrap it in useCallback to prevent anyone  else from having to actually do it in other places. And in here, this function is going  to take in the filters.
And that is going to be product filters like this. So that's simple  enough. And then based on these filters, we're actually going to do some code.
And what I'm going  to do here just to save a little bit of time, I'm just going to take some code that I already have  to make things a little bit simpler. So I'm going to come here and take this code and just paste it  here and walk you through what this actually does. So this uses the setSearchParameters function that  we got from useSearchParams.
This one receives the actual parameters that are currently in the URL.  And then based off of those, we can actually do things to it. So here we're checking the search  if we have filters.
search, if it's equal, if it's not equal to undefined, because we also  want to account for an empty string, we're setting params. search to filters. search.
And then we're  doing the same thing with the category and the same thing with the max price. And finally, we're  returning the final parameters. So that's it, that's all that we need to do for the setFilters  function.
And then the last thing for us to do inside of this custom hook is to just return all  of these values over here. So we're going to come here and we're going to do return. And we're going  to return search, we're going to return category, we're going to return max price, and we're going  to return set filters to be able to use these in different components.
So now that we have this  custom hook, everything else is pretty simple, because all of the functionality is inside of this  custom hook, which we can just use. So we can come here to the app component, we can actually get  rid of all of these state variables here, we don't need them. And we can just do const search,  category, max price equals use product filters.
And we can just call it directly. And that's it,  this component, we have no other functionality, we get the same variables over here, we're passing  them to react query in exactly the same way. And we're fine, right.
And now what we just need to do  is actually remove this on change here, because we no longer need it, we no longer need an on change  inside of this component to pass it to our product filters component. So we can just remove it and  save this. And now that's it, we're going to have an error here, because our type still expects  an on change, we're going to fix that in just a moment, this actually made our component a little  bit simpler, we did a little bit of refactoring, and our code is actually cleaner, which is a good  thing, then we can come in here to our product, this filters component, and we can do exactly the  same exact thing.
But before we do that, I just want you to keep in mind and remember that we have  this debounce search over here, which basically just delays the updating of the search until  after the user has finished typing. And it's this debounce search that we're using inside of the use  effect over here. So just remember that before we refactor.
So like we did before, we're going to  take all of this code, we're going to remove it, and we're just going to do const search, category,  and then max price. And we're going to make this equal to use product filters exactly in the same  way. And now we actually no longer need this use effect, because we no longer need to call  this on change function.
So this is redundant, we can just remove this altogether, great. And now  we can actually remove the on change here from the actual props, we even don't even need props in  this component anymore, because we have nothing to pass. So let's just remove that.
And now our  product list filters component is also simpler. So not the only thing that we have to do is just  to fix this part over here, the actual changing of the values. But that's super simple to do,  because we also have a set filters function that we can use.
So in here, what we can do instead of  set search, we can do set filters. And instead of passing a target that value, we can just pass  search, and then a target that value. That's it simple, we're done, let's do the same thing over  here, set filters, and then take all of this, and then category, and then paste this, and we're  done, we can move on, set filters, take all of this, and this is going to be the max price.
And  we're just going to set those like this. And we are done. So already with this code, we are pretty  much done.
And we have the same functionality, but now everything is being driven from the URL.  So let's see how this works. So here we have our app, it looks exactly the same.
But now if I type  something here, like product, we get to see that inside of the search variable over here inside of  the URL, actually, we have search equals product, that's great. This works exactly as we expect. If  I said a category, for example, second over here, we also get category second, if I set a max price,  like 500, we also get 500.
And our products are still being filtered correctly. What's even  better is now if I refresh this page, we get our state persisted product persist, the category  persist, the max price persist. And if we were to take this URL, send it to someone else, they would  see exactly the same screen with the same filters.
And this is efficient. This is why you want to use  the URL as state and react for things like this, because it makes sense, you're persistent, you can  reload, and you can send this to other people. And the code that we use to actually make this  work was super simple.
And it was just one custom hook to actually get this working. This is  great. Now there's one more thing that we have to fix in here, because we actually have reduced  functionality, as opposed to how it was before.
And that has to do with this use the bounce over  here. Remember the debunk search, I told you to remember that we have to deal with this. Because  now if you go to our application, you see here, we have product, every time that I change anything  on the input, we're instantly showing a search a loading over here, right?
If I just reload,  every time I type something, right, as I type every single keystroke, we're actually sending  a fetch request to the back end. And that's bad, we actually want to delay this. And that's why  we had to use the bounce before.
But now we no longer have it. So we need to reintroduce it. Now  to do that, to reintroduce it, I'm going to show you a pattern that I like to use in a lot of  my react projects, what we're going to do is we're going to come here, we're going to create  a state variable, call it const, local search, and then set local search, that is going to be  equal to use state, we're going to reintroduce you state in this actual component, we're also going  to give it here product filters, and we're going to give it here the search.
And we're going to do  what happened, let's go back, we're going to close this over here. And we're going to default this  to undefined like this, this is the first step, this is going to be the local search the search  that is only local and will only change within this product filters component, it is never  going to make it to the actual URL, then inside of our input over here, instead of setting the  filters directly, we actually want to revert to what we had before. And we want to set the local  search instead.
So we're going to do set local search instead. And we're going to put it back to  exactly how it was before. There's a reason we're doing this.
And it's because we want to have this  functionality of listening to the delayed update of our actual search. And then we're going to have  to replace the search over here with the local search like this. Now what's going to happen  with this component is this is only locally going to update, it's no longer going to trigger  the filters themselves.
And it's no longer going to trigger actually refetching the data. So  if I go back here, and I actually look at it, now when I change anything to product, nothing is  happening, we just have the local search updating, which is what we want, we want this input to  update as fast as it can locally. And then after some delay, we want to actually update the  URL with the value of the search.
And to do that, the last thing that we need to do is reintroduce  our use effect. So we're going to do use effect like this, we're going to pass it here a  function like this. Here as the dependency array, we're going to pass the bounced actually, wait, we  need to do one more thing, right, we need to use our D bounce, we're going to do const, the bounce  search like this use the bounce, and we're going to pass it to your local search.
And actually,  we made a mistake here, we need to put search, we need to default and sync the local search with the  search. So whenever we're creating this component, we are going to use the value from the URL. That's  why we have it there.
That's why we created this custom hook in the first place, we're going to  use that value in the URL, we're going to pass it here to our local state to be used as the initial  value. And then from that point on, we're going to take over the actual modifying of that value.  And we're going to handle it ourselves manually.
And again, the only reason we're doing this is  because we want to have two different states, one that is local one that is only for the component  to actually see the updates in real time. And the other one for the URL, which we get to control and  change at a later point. So then here, we're going to pass the bounce search like this.
And inside  of here, all we're going to do is we're going to do set filters. And we're going to do search.  And that is going to be debounced search like this.
So what we've done, we created this local  search over here, once again, just for this input, this is going to work correctly with the input,  the input receives the local search as a value, and then on change only updates that local search.  And then we're using the use debounce hook, which is listening to local search, it is then  going to delay the update and store it inside of this debounce search. And then we're using  it here to actually call it to our set filters, this is going to then set it and commit it to  you to the URL, which is going to trigger all of the other functionality.
We're only doing  this because we want the local search and the search in the URL to not be in sync and to  have different values. So we have to do this, we have to add this complexity to our component.  So now I can come here to my input, I can actually change.
And as long as I'm changing, notice  that the input itself is actually changing, I'm just doing it super fast. But as soon as I stop  changing it, then does the URL actually change, right? Again, I can type anything, the URL is  not in itself changing, as you can see here, the moment I stop, then that change gets committed  to the actual URL, right.
So we have two different state variables for two different purposes. But  they're synced together in a way that we control. That is why we're using this code over here.
I  hope that you get it. And that's it. With this, you've actually learned a lot in this video,  you've learned how to correctly create a custom hook, put all of the URL related functionality  to it, you've learned how to get and derive your state from the actual URL, and then set it in  the URL over here.
You've also learned how to use callback whenever you're putting functions that  have custom hooks that are meant to be reused, which is a great thing to always do. And then  we've simplified this component over here by just pulling the values from the URL through  the custom hook, we've added our own custom functionality over here, which again, we had to,  and we've made our app component really simple, but with the same functionality, right, that is  a sign of a good developer, that is the sign of efficient code. And that is what you should do  as a developer, you should always strive to keep things very simple to improve things as much as  you can and to write good and clean code, which this is a great example.
And what I would recommend  that you do is you take this code, you can find the repository for it in the description, play  around with it, see how this works, really try to understand it because there's a ton of lessons  to learn here and hopefully that is going to make you a better React developer. If you enjoyed this  video and you want to see more videos just like this one, make sure to leave this video a big  thumbs up. You can also click here to subscribe or you can click here to watch a different video  of mine that YouTube seems to think that you're really going to enjoy.
And with that being said,  my name has been Darius Cosden, this is Cosden Solutions. Thank you so much for watching and  I will see you in the next one. Ciao, ciao.
Copyright © 2025. Made with ♥ in London by YTScribe.com