Using URLs as a State Manager

Using URLs as a State Manager

State is a very common word in React, which refers to an object that represents the current condition of a component. A component's data can change over time due to user actions and network responses. However, state serves as the single source of truth for a component's data and helps react to cause re-renders whenever it changes. A popular example is the counter app, where a user clicks a button, the state is incremented by one, and React immediately re-renders and updates the User Interface (UI) to reflect the new state value. State can be managed in React in many ways, including using State management tools like Redux, Zustand, Jotai, etc. However, the most common way to manage state is by using the React useState hook, which takes in one argument and returns an array containing the current state value and a function to update that state.

import React, { useState } from "react";

export default function CounterComponent() {
  const [count, setCount] = useState<number>(4); //set initial state

  return <div>{count}</div>; //display state data
}

Using URL for State

In addition to the approaches we have covered, there is another approach that, in some use scenarios, may be preferable. The website's URL is the access point to that state management tool. A URL is the address of a given unique resource on the web and those resources can be HTML pages, CSS documents, videos, images, etc. In our case, we will be focusing on the URL of our HTML pages generated by React. Also, as an example, we will be building a simple mock online store app in Next.js with the state stored in the URL.

Advantages of using URL

  • Bookmarking and Sharing: When we use URLs to manage states, it becomes very easy to share specific states of our application with others. An example is how you can navigate to this exact link and find the exact application state that I made: jumia.com.ng/phones-tablets/sony/?screen_si... That gives you a list of all Sony tablets and phones with screen sizes of 6.1 inches and seller scores of between 4 and 5 stars on the Jumia online store.

  • Browser Navigation: Storing app states in URLs also makes for a great user experience with browser navigation because a user can simply use the browser's navigation controls (back and forward buttons) to navigate through different states of the application. This would be a challenge to implement with other state management methods

  • SEO Optimization: A big advantage for the marketing team, Search Engine Optimization (SEO) ensures that a website ranks well in search engine results. Employing URLs for state management can help create search-engine friendly URLs that reflect the state of the application. These improve visibility and discoverability of a website on the web

  • State Decoupling and Scalability: When we use URLs to manage state, we are effectively separating state from the application logic, which means a more modular and scalable architecture. Since the individual components are not responsible for managing the state, it becomes easier to maintain and even extend the application over time.

Example App in Next.js

Let's set up a very simple Next.js application that uses URLs for state management. It gets the brand, model and processor class family from the URL and displays them on the page

  • First, we bootstrap the Next.js app with custom preferences
npx create-next-app@latest laptop-store

  • Then we navigate to the app folder of the project and then create a new route named product in the app directory
cd laptop-store/src/app && mkdir ./product
  • Next, we can create a page.tsx file inside of the product route, which will serve as the component that renders when that route is navigated to in the browser. I use emacs but you can use any text editor you prefer.
touch page.tsx && emacs page.tsx
  • Then we can create the component and import the useSearchParams hook, which is a Next.js hook that lets us read the current URL's query string. Take note that the search parameters returned are read-only, so you can directly modify them using the hook. We use the use client directive at the top of the file to specify that it is a client component since hooks (like useSearchParams) don't work in server components.
"use client";

import React from "react";
import { useSearchParams } from "next/navigation";

function ProductPage() {
  const searchParams = useSearchParams();


  return (
    <div>
    </div>
  );
}

export default ProductPage;
  • Now, we can access the query parameters using the searchParams object we have initialized. In our case, we expect the brand, model, and processor names. We also use the Nullish Coalesing operator (??) to provide default values, just in case we do not have them set in the URL
  const searchParams = useSearchParams();
  const brand = searchParams.get("brand") ?? "Not Specified";
  const model = searchParams.get("model") ?? "Not Specified";
  const processor = searchParams.get("processor") ?? "Not Specified";
  • Finally, we can add the values to the JSX and add a little bit of styling with Tailwind. The final code looks like this:
"use client";

import React from "react";
import { useSearchParams } from "next/navigation";

function ProductPage() {
  const searchParams = useSearchParams();
  const brand = searchParams.get("brand") ?? "Not Specified";
  const model = searchParams.get("model") ?? "Not Specified";
  const processor = searchParams.get("processor") ?? "Not Specified";

  return (
    <div className="flex justify-between border border-white m-3 p-3 rounded-md font-bold">
      <h3>BRAND: {brand?.toUpperCase()}</h3>
      <h3>MODEL: {model}</h3>
      <h3>PROCESSOR: {processor}</h3>
    </div>
  );
}

export default ProductPage;

Conclusion

URLs are not a complete replacement for other state management methods, and there will be occasions where you just have to whip out the good old useState hook, but the takeaway from this is to know that storing state in URL can be advantageous and even a better alternative in many cases.