Go back

You Might not need a state-management Library

I have never been a fan of most state-management libraries in React's ecosystem. This is because Redux felt too convoluted for me to fully grasp when I first used it in 2020.

Although, When I gave Zustand a try for the first time. It felt intuitive to me. But, that's not in the scope of this post.

I've been experimenting with next/router for, say, a month and a half now. Why? Because I might not need a state management library for some of the features I was working on, in the projects I am currently contributing to.

During the early stage of the projects, the thoughts of state management kept on ringing in my head, which in turn troubled me, because I know I'm already rusty with Redux, so, yeah, anxiety sets in.

But then, I took solace in a particular quote (more like the title of an article) from Dan some years back — You might not need Redux

One thing I do whenever I embark on a project is to outline what I'd want the project architecture to be like, what dependencies I'll use, how to approach separation of concerns etc.

I see quite an amount of people tapping into the use of these tools for projects that may not be entirely dependent on such use. There's no harm in doing so, to be honest. But, such a situation can be likened to a hunter that tries to kill an ant with a gun.

From React's ecosystem

I still consider myself a beginner a lot of the time, maybe that is why I try not to bite above what I can chew, so I take my time properly to learn about some things in this field.

React.useState has always been my go-to approach when dealing with client-side states in any project. Well, you could say, that all I've worked on were, and are still baby projects — not entirely large projects — and you'd be absolutely correct.

But, the last project I embarked on, pushed me to go beyond React.useState. It got to a point where the project started having deeply nested components, and the data they receive are dependent on the application's state.

How do I manage this? I pondered — vainly!

I knew it is possible to actually keep state in the browser's URL. I mean... that's how Google's search manages the UI state of your search results. But, I had no idea how to approach this practically.

Onto next/router

I can say that a lot of things have been preparing me for this "Technical memoir" because the first time I actually tried doing something related to manipulating a URL was in this project — year in review — where I had to work on implementing a filterByYear functionality.

When anyone selects a particular year, the URL is updated with the query parameter of that year with a slug representing its value.

That alone establishes a deep linking system that enables a global state. When you share that link, anyone that supposedly visits the page will see the same content whose state is dependent on such a query.

This commit message shows my first attempt at this pattern of preserving state. I learned later on that it had a side-effect which Dayan Ruiz went on to fix in this Pull Request

From there on out, I've improved a lot on the use of this approach that Next.js' router enables.

One thing I built, and one that I am really proud of, is react-tab — A tab component that preserves the state in the browser's URL — It took me quite a while to be able to make it work.

You can take a look at the source here, perhaps, you'd find something worth learning there. I'm working on adding support for React projects that do not depend on Next.js though.

A real-life example.

So, after I had established a proof-of-concept for this pattern, I tried implementing this same feature on my blog.

There's a button that you can click on to load more posts and works as intended. But, that's just it. When you reload the page, and the component re-renders, the state is lost.

With next/router and learning from the approach I built react-tab upon, I tried simulating the experience and it worked fine. The snippet below shows how Implemented it.

const initialPosts = 7
const incrementInitialPosts = 4
 
const loadMore = () => {
  const totalPosts = Number(displayPosts) + Number(incrementInitialPosts)
  router.replace({
    pathname: router.pathname,
    query: { ...router.query, posts: totalPosts },
  })
 
  setDisplayPosts(totalPosts)
}
 
// preserve when there's a change in the query or the current path.
React.useEffect(() => {
  const { query } = router
  const { posts } = query
 
  const totalPostsFromQuery = posts || initialPosts
 
  setDisplayPosts(totalPostsFromQuery)
 
  setSearchResults(updatedPosts.slice(0, totalPostsFromQuery))
}, [router.query.posts, router.pathname])

I have an article on how I implemented this at first, you can take a look at it here.

The important part of the snippet above is the useEffect. It plays an important role which ensures that whenever there's a change in the query parameter, the articles are updated accordingly.

Normally, the URL of my blog would appear like this meje.dev/blog?posts=11 when you click on the button which in turn triggers loadMore and by summing initialPosts and incrementInitialPosts, the value of query parameter, posts, becomes 11.

So if you manually update the value of posts, say, to 13, two more articles will be added to the UI. Furthermore, the router.pathname value in the Effect's dependency array ensures that the state is preserved when you navigate to and from the page.

Alternative approaches.

There are other thought processes for achieving this global state management with the browser's URL. Some resources that I found helpful are listed below. You should take a look at them in your spare time.

How and Why You Should Store React UI State in the URL

How To Use URLs to Store State in a React App