Things I learned when I finish my first Next.js project
Tags: webdev, nextjs, devjournal, todayilearned
Published: 2022-04-21 07:40:49

I decided to learn Next.js few months ago, partly because I heard people talking about it, but mostly because I wanted to learn new thing and improve my knowledge. So I opened Next.js documentation and read its concept. I was so impressed of its features that I created a new Next.js project and started coding right away. I already had an idea so that was the perfect time to implement it. There are things that you only encounter when practicing, and you will learn a lot not only from those things themself, but also the related ones. And here are the things that I've learned while doing my Next.js project.

Before we begin, here is my project: [KEYREVEAL](https://github.com/PhanDungTri/keyreveal)

## Rehydrate DOES NOT work like Render

In the early days into the **Server-Side Rendering** world, for me, everything wasn't different so much from **Client-Side Rendering**. When finding resources on internet, I've encountered the term _"rehydrate"_, but at that time, I didn't care really much about it. I thought it was as same as rendering. Until some thing unexpected happened.

```jsx
const Component = () => {
  const data = useLocalStorage("saved");
  
  return data ?
    <Text>Data is: {data}</Text> :
    <Alert>There is no data</Alert>;
};
```

This component will render the data retrieved from `localstorage` or show an alert if the data is empty or doesn't exist. `useLocalStorage` hook accesses the `localstorage` then returns the value of the passed key, if it can't find the key or the `localstorage` isn't accessible, then it returns `undefined`.

Because the `localstorage` isn't available on the server, it will form a page with an `Alert` then send that fully-formed HTML to the client. The HTML will be shown on the screen as soon as the client receives it. But this HTML is not fully interactive, it needs React to render and attach event handlers to it. This process is called **rehydrate**.

`localstorage` can be now accessed by the client and we assume that `localstorage` contains a key `saved` with value. So `Component` will be rendered with `Text` instead of `Alert`.

The problem is this component is breaking the rules. In the development environment, React will warn you about this behavior.

![React warns about mismatched DOM](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yu33fydnkuokf8r4yz1h.png)

When React performs rehydration, it will reuse the DOM which is sent from the server. The rehydration process is optimized to be fast, it assumes that the DOM structure won't change so that there is no "spot-the-differences" job happens, it just tries to fit its DOM with the existing one.

Although React sometimes can handle this behavior very well, but in some cases, this will lead to bugs like the component is rendered in the unwanted place. If you intentionally want to render things different on the server and the client, there is a solution called two-pass rendering.

## Two-pass rendering

In two-pass rendering, the client and the server will render the identical DOM. After the rehydration process, the client will start rendering the differences.

```jsx
const Component = () => {
  const data = useLocalStorage("saved");
  const [isClient, setIsClient] = useState(false);

  
  if (isClient)
    return data ?
      <Text>Data is: {data}</Text> :
      <Alert>There is no data</Alert>;
  else return <Loading />;
};
```

As you can see, both server and client will render the `Loading` because `isClient` has the same `false` value in two environments. The client and the server now use the same DOM structure, so how do we render the `localstorage` data?

You probably know that `useEffect` only happens in the client, so to render the `localstorage` data, we can set `isClient` to be `true` in `useEffect`.

```jsx
useEffect(() => setIsClient(true), []);
```

We only want this run once on the first render. React will recognize the update and perform re-rendering. The downside of this solution is the application needs to render the UI twice before it's fully interactive. But in the most cases, this isn't the big deal.

## Component Libraries is not that bad

In the past, when working with component libraries like React Bootstrap, Material UI or Ant Design, I felt like I was put into an inconvenience small box. I thought I had to follow their rules and my creativity was imprisoned. Because of that, I designed the UI from scratch for most of my school project. And of course, the quality is just acceptable for... the school project, but I was pleased with it. Honestly, building the UI by myself taught me a lot of things about CSS and UI design that I would never learn from using Component Libraries.

![My UI](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1kms6vwqth6r8pvkqhsm.png)

> Spend days writing CSS for the UI of my school project

Yep, it took me ages to complete the UI, so that I don't want to write all CSS by myself in this project, I want to give component libraries another shot.

I avoid Material and Bootstrap because I had bad experiences with them in the past and I also want to try a new thing. Geist UI is my first choice. It simple and I like its design. But then I realized it didn't meet my project requirement. I still have to write a lot to make it fit my project.

Once time I was surfing Reddit, I found a post in `r/reactjs` announcing a new release for a component library. Coincidentally, I was looking for a new component library at that time, so why don't give it a look.

![Mantine UI](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2297gjnlr6igq0hc7p3w.png)

It was **Mantine UI** and it was beyond my expectation. The design is modern and beautiful. There are a lot of pre-built components, but not redundant, and useful hooks that can satisfy your needs. Each component has styles API which supports styling of each component part, this makes all Mantine components highly customizable. Mantine provides some extensions likes modals manager, notifications system, rich text editor,... so that you don't have to spend time building your own. I have a good experience when working with Mantine, every thing is so smooth. Honestly, it's my most favorite component library so far.

## Use FormData to increase the performance of the form

When I see a React tutorial on the internet, if it uses `form` or `input`, highly chance it will use them as controlled components, which means each `input` will have its own state.

```jsx
const Form = () => {
  const [name, setName] = useState("");
  const [address, setAddress] = useState("");

  const submit = (e) => {
    e.preventDefault();
    console.log(`${name} lives at ${address}.`);
  };

  return <form onSubmit={submit}>
    <input
      placeholder="Input name..."
      value={name}
      onChange={(e) => setName(e.currentTarget.value)} />
    <input 
      placeholder="Input address..."
      value={address}
      onChange={(e) => setAddress(e.currentTarget.value)} />
    <input type="submit" />
  </form>;
};
```

The downside of this is when you type a single character in either of those `input`, it will make the whole `Form` re-render. It isn't a problem with small and simple form. But if you have a big and complex form, especially the form with dynamic inputs, you will notice the lag. It takes a bit longer to reflect the updated value of the input to the screen. Another downside is you have to create a bunch of states for each input in your form, but this can be solved by using `useReducer` hook.

If your form just basically collects the user data, then Javascript has a powerful built-in `FormData` interface that helps you get rid of those `useState`.

```jsx
const Form = () => {
  const submit = (e) => {
    e.preventDefault();
    const fd = new FormData(e.currentTarget);
    console.log(`${fd.name} lives at ${fd.address}.`);
  };

  return <form onSubmit={submit}>
    <input placeholder="Input name..." name="name" />
    <input placeholder="Input address..." name="address" />
    <input type="submit" />
  </form>;
};
```

No state, no re-rendering. Don't worry, your input data is still shown and updated in the `input`. The form is now uncontrolled.

## The input doesn't necessarily live inside the form

Every thing inside a form, obviously, belongs to that form. So that the `FormData` constructor will know what fields the form has and the submit button will know it sends the event to which form.

But in some cases, the input can't be inside the form, how can we make it belong to that form? There is an attribute named `form` that specifies which form the input belongs to.

```jsx
<form id="user-form" onSubmit={submit}>
  <input placeholder="Input name..." name="name" />
  <input placeholder="Input address..." name="address" />
</form>
<input
  placeholder="Input password..."
  name="password"
  type="password"
  form="user-form" />
<input type="submit" form="user-form" />
```

With this attribute, you can put the input anywhere and it still belongs to the specified form.

# Conclusion

There are still many new things I have learned when doing this project, but maybe I will share in another post. For me, practicing is the best way to discover and learn new lessons. You keep digging into it, the programming world will keep expanding.