My fair share of rehydration in React.
Tags: javascript, react, nextjs
Published: 2022-10-22 12:32:32



I've always been fascinated by the kind of work that people like [Kent C. Dodds](https://twitter.com/kentcdodds) and [Josh W. Comeau](https://twitter.com/JoshWComeau) put out there in the field of <span className="get-reader">Developer Education</span>. This article encompasses how my curiosity almost landed me in a very deep and recursive process of debugging to no avail for almost two days, but, that's sort of what we do right? &mdash; getting stuck over and over until we find a possible fix to a problem. LOL!

Okay, enough with the story, _"My fair share of rehydration in React"_ you'd have probably muttered to yourself. Well, all these started when I took a look at [Kent's Blog](kentcdodds.com/blog), and I loved how simple the UI of the featured-article card appeared, but that was not the only thing. I figured out that when I refreshed the page, the featured article changed, immediately, I made the _"wow this looks sooo cool"_ face.

I thought to myself, I can recreate this on my blog too, little did I know that it will appear a little bit more complex than how I first perceived it to be, then. Nonetheless, I embarked on this arduous journey.

## Thinking in React

I read the first module &mdash; [Thinking in React](https://beta.reactjs.org/learn/thinking-in-react) &mdash; on the beta version of React's documentation website by [Dan Abramov](https://twitter.com/dan_abramov). The whole idea behind &mdash; or at least, what I perceive it to be &mdash; this, is for us to be able to understand and develop a mental model for problem-solving, that way, we can always attempt to proffer solutions to similar problems without having so many blockers, rather than just jumping into the process of writing the code.

Establishing the mental model, or pattern of implementing this feature took almost three pages in my notebook, as simple as the solution is, yes. I see my mental model as a decision tree, though. The first thing I knew that I had to do was to create an entirely new UI that would take in the featured-article data which I'm pulling with Next.js' server-side data-fetching method, `getStaticProps()`.

Creating the layout of that component wasn't the issue, and neither was the process of returning the latest article by selecting the first index in the array of blog posts. I recalled that the moment I refreshed Kent's blog, the featured article is a new one, completely different from what was there during the initial page load.

I became awestruck, again! How do I implement this exactly? I figured out that the best solution I can use for the implementation of this feature was to rely on JavaScript's `Math.random()` method. I'll pull data from the `posts` prop via `getStaticProps()`, assign it to a variable, and multiply it by the length of the array.

```js
export default function BlogPage({ posts }) {
  const randomFeatured = Math.floor(Math.random() * posts.length)
  let featuredPost = posts[randomFeatured]

  return (
    <React.Fragment>
      <FeaturedCard data={featuredPost}>
    </React.Fragment>
  )
}
```

The `randomFeatured` variable returns a number between `0` and the length of the array. Say, for example, I have a total of ten blog posts, and I'll be getting a number between `0` and `9`. Next thing was to access that random number with the `posts` array, like so;

```js
let featuredPost = posts[randomFeatured]
```

_"Whew! that was easy"_ was the phrase I muttered to myself until I got stuck. But, let's not go there yet. I decided to make my whole idea behind the implementation of this feature to be around this: <span className="get-reader">I want people to come to my blog and read the articles there without having to keep scrolling until they find what they want. Fine, the idea of a featured article brings gets the attention of people, but I also want that `<FeaturedPost />` component to be dynamic.</span>

That's the whole essence of the randomly generated featured posts. With that step out of the way, I thought of another feature to add to the overall process of rendering the rest of the articles, I wanted a way to render the remaining articles without the featured article in this list.

Wow! It felt really simple &mdash; at least, that was what I thought. I was wrong, again! My original approach towards accomplishing this goal spiraled &mdash; aimlessly &mdash; around the usage of so many unnecessary array methods like `.indexOf()` &mdash; to get the current of the featured article, `Array.prototype.splice()` &mdash; to mutate the content of the original `posts` array by checking if the properties of the `featuredPost` object were present in it. I ended up having something similar to the snippet below.

```js
let featuredPost = posts[randomFeatured]

const indexOfFeaturedPost = posts.indexOf(featuredPost)

if (indexOfFeaturedPost > -1) {
  return posts.splice(indexOfFeaturedPost, 1)
}
```

Ideally the `.splice()` method returns `1` when the condition in the `if()` block, above, evaluates to true, and `-1` when it is `falsy`. But, this approach was <span className="get-reader">wonderfully flawed</span>, YES, it was. <span className="get-reader">Why?</span> you might ask me. Although this seemed to work at first, whenever the components are re-mounted in DOM, the `.splice()` method repeats the same process and keeps removing any featured article from the list. <span className="get-reader">I was furious and frustrated, and I think I screamed too!!!</span>

After several attempts toward losing it all. I tried using the `Array.prototype.filter()` method, &mdash; one I should have utilized from the get-go. This method provided everything I needed for the success of this feature.

```js
const updatedPosts = posts.filter((object) => object.id !== featuredPost.id)
```

Initially, the `frontmatter` of the articles on my blog do not have `id` property in them, so, for me to be able to implement this without any blockers, I had to make sure that these `id`s were consistent throughout all the articles frontmatter. In the snippet above, you'll notice that I'm comparing the `id` of the featured article with the rest of the array content in the `posts` variable, and proceeding to return a new array &mdash; `updatedPosts`

With that sorted out, I moved on to use React's `useEffect()` hook and the `setTimeout()` function to update the featured article, after thirty seconds. Although the featured article gets updated when you navigate to another route on my website, and back to the `/blog` route, since the variable is in an `effect` lifecycle method, which gets called whenever a component is mounted on the DOM.

```js
React.useEffect(() => {
  setTimeout(() => {
    featuredPost
  }, 30000),
    updatedPosts
}, [])
```

I went ahead to add the `updatedPosts` variable in the lifecycle method too. Everything seemed fine, I was super pumped that I had been able to accomplish something meaningful &mdash; If only I had known that the next set of outcomes would be disastrous.

# My travails with rehydration

As I said previously, little did I know that the constant update of the featured article would result in a rehydration error, take a look at it in the image below. I had zero knowledge about the consequences that my approach would yield, and this error spanned out of the <span className="get-readers">Server-side Rendering</span> approach that I'm using to get my articles with Next.js' server-side data-fetching methods: `getStaticProps` and `getStaticProps`.

![rehydrattion error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cz09up4iybfjxfiohv1c.png)

I needed to understand why this kept happening because I can remember clearly that wasn't the first time it's happened to me. I knew beforehand that Next.js uses a <span className="get-reader">SSR: Server-side Rendering</span> technique to optimize the experiences we ship to people around the world, when they use the software we've created, on the web.

Before the advent of Frameworks like [Next.js](https://nextjs.org) and [Gatsby](https://gatsbyjs.org), the rendering process of React applications occurs on the client-side &mdash; in the browser. Meaning that, when you load or visit a React application, the browser gets an initial HTML document which is due to the inner workings of something similar to this snippet below, in a React app, bootstrapped with [CRA: create-react-app](https://create-react-app.com)

```js
import ReactDOM from 'reacct-dom'
import App from './App'

ReactDOM.render(<App />, document.querySelector('#root'))
```

On initial load, the page is empty until some of the scripts that provide functionality have been fetched and parsed by the browser. While this &mdash; the parsing process of scripts &mdash; is going on, React is creating a representation of how the page would look like, when the render-blocking scripts have been parsed successfully.

But, this process &mdash; between the browser and React &mdash; takes time, which in turn leads to a poor user experience for people, because while this is going on, the user gets to see a blank screen alone.

With <span className="get-reader">server-side rendering(SSR)</span>, the user, or whoever visits your site or app, gets a full HTML document from the server, instead of the empty one with a bunch of scripts that needs to be parsed by the browser. With the <i><span className="get-reader">server-side rendering</span></i> approach, the user is presented with a HTML &mdash; sometimes with the styles too &mdash; document that they can look at, while the browser and React does their thing.

Back to the problem on ground now, shall we? The error message in the image above says <span className="get-reader">"The text content does not match the server-rendered HTML"</span>, and this is a result of the random featured article cards I'm rendering, which in turn causes a mismatch between the server-rendered HTML and this new version of the page, every <span className="get-reader">thirty seconds</span> and every time the user navigates away, and back to the `/blog` route.

## A proposed solution

So, how do I fix this? was the question I asked myself. Knowing that the problem was related to <span className="get-reader">hydration</span> was the first step towards my liberation, I started making my research, and I found this [article](https://www.netlify.com/blog/fix-next-js-react-hydration-error/) written by [Jason Lengstorf](https://twitter.com/jlengstorf).

It had the exact error message as the title of the article, I felt so happy at that moment, not until I read through it and figured out that, his approach did not suit my use-case. His approach involved using [Next.js Advanced Middleware on Netlify](https://docs.netlify.com/integrations/frameworks/next-js/middleware/?_ga=2.245092418.671575324.1666434561-795446949.1651424539) and writing Netlify edge functions to transform a page on the fly, at the edge.

But, I stumbled upon an article &mdash; <span className="get-reader">The perils of rehydration</span> by [Josh W. Comeau](https://twitter.com/joshwcomeau) where he buttressed on the idea of rehydration does not equate to be another render, the idea behind <span className="get-reader">Server-side Rendering</span> and some other things I can't remember. You can take a look at the article [here](https://https://www.joshwcomeau.com/react/the-perils-of-rehydration/).

The solution looks similar to the snippet below, where a state variable is initialized &mdash; in the component that will be rendered &mdash; and is set to be `false` by changing the value of the variable in the `useEffect()` hook to be `true`, we trigger a re-render with the content of the component.

```js
const ComponentToBeRendered = () => {
  const [isComponentMounted, setIsComponentMounted] = React.useState(false)

  React.useEffect(() => {
    setIsComponentMounted(true)
  }, [])

  if (!isComponentMounted) {
    return null
  }

  return (
    <ContentOfComponent>
  )
}
```

## Improving user experience

In the solution that <span className="get-reader">Josh</span> proposed, I figured out that it caused a layout shift on my blog whenever the featured article is yet to be mounted. The GIF below illustrates that and this is due to the `null` value that was returned in the `if()`.


![layout shift](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0jlyxtte9a2g9br9yz18.gif)

Instead of returning `null`, I decided to create a skeleton loader that will give the user or the readers an idea of what to expect concerning the layout of my blog.

```js
if (!isComponentMounted) {
  return <LoadingFeatured />
}
```

With this solution, I was able to bypass the rehydration error in React and provided a good UX by returning a loading skeleton.