Next.js: the new normal
Tags: webdev, react, nextjs, shortnotes
Published: 2020-08-31 12:58:26

Next.js is a full-stack framework based on React.js.

### What it offers:

- Prerendering: the entire HTML is created in the server and send to the client. So the client receives the HTML rather than the JS file. Once the HTML(string) is available it gets rehydrated at the client side. One can think of _rehydration_ as adding event listeners and making it interactive. All the routes are pre-rendered by default.


##### Scripts

The common commands used to run and build a next project are the following.

```
"scripts":{
  "dev": "next",   // start in dev mode
  "build": "next build", // start production build
  "start": "next start" // run production build
}
```

### Routing

- using `reach-router` under the hood

- file-system-based routing

- for that, we create a special folder called `pages`

- all the folder/file names become the routes for those files

- we can handle dynamic routes and receive the parameters like `notes/:id`. For that we just need to create a file named `[id].js(x)` inside a _notes_ folder. If the _notes_ folder has an _index_ file it will be treated as the `notes/` route

- to use the param inside the render function we can use `useRouter` hook from `next/router`. For classes, you have `withRouter` HOC.

`notes/[id].js`

```
import { useRouter } from 'next/router'

function App(){
 const router = useRouter()
 const {id} = router.query

 return (
    ...
 )
}

export default App
```

_note_: In React, functional components are actually the render function. The entire function is the render method in case of functional components. With classes, we will have an explicit `render()` with a return value. 

- if you want to handle slugs, like `notes/shihab/1`, you can have a file named `[...slug].js` inside the _notes_ directory. This time the router query will return an array-like `['shihab',1]`. Even with catch-all routes, the index will still be used.

### Navigation

#### Link component

For navigation `next/link` expose a `Link` element. It is always for *client-side* routing. That means, on navigation, this will not trigger a network call. 

```
import Link from 'next/link'

function App(){
  ...
  return {
    ....
    <Link href='/notes/[id]' as={`/notes/${id}`}>
      <a>Note</a>
    </Link>
  }
}
```

`as` path will be the exact path URL, the `href` will be the file's relative location. The `href` prop takes a page name as it is in the pages directory. For dynamic routes, you will need the `as` prop as well.

You must have an _a_ tag as the child of the Link component, but the _href_ lives on the Link.

For server-side routing, you can readily use an anchor tag like `<a href='/docs'>Server side routing</a>`

#### Programmatic routing

In order to navigate from code, one can use `router.push()` from `next/router`'s `useRouter` hook. 

```
import { useRouter } from 'next/router'

function naviagteOnSuccess(){
 const router = useRouter()

 ....
 router.push('/notes/[id]',`/notes/${note.id}`)
}
```

### Styling

- if you are using global CSS, `pages/_app.js` is the only place you can import it. If you try to import it in other places Next.js will throw an error. This is more tied to the bundling of styles and loading them

- Next.js readily supports _CSS Modules_. With CSS modules we get file scoped styles. How it works is, with each import of CSS module file, a file specific class name gets added(prepended) to the classes you use. So the style you use is specific to that particular file and doesn't collide with others. The CSS modules will only work with non-pure selectors like classes and ids, etc and not with element selectors(div, span, p,...). The filename should be like `file-name.module.(s)css`.

### Special files

#### `_app.js`

- if you want to hijack the entry file of Next, `_app.js` file is the place. If you want to inject global styles, props or anything, it should happen here. This `_app.js` is automatically created for you out of the box if you don't.

#### Next.js config

- `next-config.js` in the root of the project

#### TS support

- Just create a `.tsconfig.json` in the root.
- Next will ask you to add some libs and dependencies. Add them.
- Bhoom, now Next will auto-populate the _tsconfig_ for you. No more traction in setting up TS.

### API routes

- Next is a full-stack framework. You can have your API route handlers inside a directory `pages/api`.
- The routing is the same as that for pages.

### Data fetching

- by default fetch is available

Data can be fetched on the server and the client. Client-side data fetch is the same, what we do in a normal React app. The components may be rendered in the Server, but data fetching will only happen on the client in this case. That means, if you fetch the data in the client(using hooks or lifecycle methods), they aren't triggered on Server. The Server will render the view with the components initial State, that's all. *No, waiting until the client fetches or manipulation is over.*

To fetch data on the server we have
- getStaticProps
- getStaticPaths
- getServerSideProps
- getInitialProps

- All of the above methods are only meant to run on the server(except getInitialProps, during subsequent calls).
- they are not even added to the client bundle
- these methods can access DB, file system and all the things that can be done on the server-side
- the return value(objects) of these methods are injected into or send to the client-side components as JSON files

#### getStaticProps

- to pass down any static props to the components, that are available during the build time
- it may receive the props from the _getStaticPaths_ method
- the return value is always an object
- this object is available as the props inside the component
- when building dynamic pages you will have the `params` passed from _getStaticPaths_, inside the _getStaticProps_
- it is only called once at the build time (when building the app using `next build` command)

```
export async function getStaticProps(context) {
  return {
    props: {}
  }
}
```

#### getStaticPaths

- if you want to generate static pages you can use this method
- it should return an array of `paths`
- the pages are created for the paths at build time
- if the pages need some data to be fetched, we use the _getStaticProps_
- it might not be required to statically generate all the pages in advance, so you can opt for runtime _SSR_ using `fallback: true` 
- by using fallback you can show some loaders if required when the page is being built

```
export async function getStaticPaths() {
  // get all the paths for your posts from an API
  // or file system
  const results = await fetch('/api/posts')
  const posts = await results.json()
  // create the paths array
  const paths = posts.map(post => ({params: {slug: 
  post.slug}}))
  /*
  [
    {params: {slug: 'get-started-with-node'}},
    {params: {slug: 'top-frameworks'}}
  ]
  */
  return {paths}
}

export async function getStaticProps({ params }) {
  const res = await fetch(`/api/post/${params.slug}`)
  const post = await res.json()
  return {
    props: {post}
  }
}
```

#### getServerSideProps

- called on every request on the *server*
- used if you want to do some data fetching for dynamic SSR routes
- you will have access to HTTP header, query params,req and res headers
- even if it is client-side navigation, this method is triggered on the server-side and data is sent down. This is actually an extra roundtrip 😢.

```
export async function getServerSideProps() {
  const response = await fetch(`https://somedata.com`)
  const data = await response.json()

  return { props: { data } }
}
```

#### getInitialProps

- not recommended as per docs, but not yet deprecated 💪
- on Server-Side Rendering(SSR) pages it is run on the server and data is passed down as JSON
- for Client-Side Rendering(CSR) it runs on the client
- used to fetch data

Note: _when the page is fetched upon URL/address bar navigation, it is SSR. On client side navigation it is CSR._

### When to use what

- Do you need *data at runtime* but *don't need SSR*? Use *client-side* data fetching.

- Do you need *data at runtime* but do *need SSR*? Use *_getServerSideProps_*

- Do you have pages that rely on data that is *cachable* and *accessible at build time*? Like from a CMS? Use *_getStaticProps_*

Do you have the same requirement as above but the *pages have dynamic URL params*? Use *_getStaticProps_* and *_getStaticPaths_*

### Rendering modes

Basically 3 rendering modes

- Static: pages are built at run time.

- Server Side: page are built on each request and cached after the initial hit

- Client-side: the rendering happens on the client. The server will not send the HTML markup string. By default, the pages are pre-rendered while using Next.js.

The type of rendering is chosen based on the data fetching strategy we choose(mostly). By default, the pages are pre-rendered by Next. Pre-rendering means, the server sends down an HTML markup string to the client. Once the request is received the client will try to make it interactive by injecting listeners and handlers (hydration).

By choosing the appropriate data fetching strategy we can decide the rendering mode for the app.

If your component works with 
- DOM APIs
- only on client data,
there is no point in server-side rendering them. We can opt-out of SSR by using,

```
const NoSSR=dynamic(()=>import('../component'),{
 loading:()=><div>Loading.....</div>,
 ssr:false
})
```

Here `<NoSSR/>` will always be client rendered.

### Deployment

By default, it requires a Node.js environment. By using `next export`  we can create a pure static build from our Next project and server it.