Quick Nextjs

Posted on October 2, 2021
Tags: javascript

1 Extensions setup

2 install

npm install --global create-next-app  
npx create-next-app

npm install bootstrap

npm install --save-dev typedoc-plugin-missing-exports
npm install --save-dev typedoc-umlclass
npm install --save-dev typedoc

3 Setup bootstrap

import "bootstrap/dist/css/bootstrap.min.css";
import type { AppProps } from "next/app";
import { useEffect } from "react";

export default function App({ Component, pageProps }: AppProps): any {
  useEffect(() => {
    require("bootstrap/dist/js/bootstrap.bundle.min.js");
  }, []);
  return <Component {...pageProps} />;
}

4 Setup configs

npx eslint --init
? How would you like to use ESLint? … 
  To check syntax only
  To check syntax and find problems
▸ To check syntax, find problems, and enforce code style
? What type of modules does your project use? … 
▸ JavaScript modules (import/export)
  CommonJS (require/exports)
  None of these
? Which framework does your project use? … 
▸ React
  Vue.js
  None of these
? Does your project use TypeScript? ‣ No / [Yes]
? Where does your code run? …  (Press <space> to select, <a> to toggle all, <i> to invert selection)
▸  Browser
   Node 
? How would you like to define a style for your project? … 
▸ Use a popular style guide
  Answer questions about your style
? Which style guide do you want to follow? … 
▸ Standard: https://github.com/standard/eslint-config-standard-with-typescript
  XO: https://github.com/xojs/eslint-config-xo-typescript
? What format do you want your config file to be in? … 
  JavaScript
  YAML
▸ JSON
? Missing packages. Would you like to install them now? ‣ No / [Yes]
npm install --save-dev prettier
npm install --save-dev eslint-config-prettier
npm install --save-dev eslint-plugin-prettier
npm install --save-dev eslint-config-next 

4.1 files

  • make a typedoc.json file and .env file
{
    "$schema": "https://typedoc.org/schema.json",
    "entryPoints": ["./pages/*.tsx"],
    "out": "docs"
}
ghpageURL=mygithubrepoName
#comment the above out if running locally
  • modify next.config.js to below so that the relative path works with ghpages
/** @type {import('next').NextConfig} */
const ghpageURL = process.env.ghpageURL ? `/${process.env.ghpageURL}` : undefined;

const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
  assetPrefix: ghpageURL,
  basePath: ghpageURL,
  images: {
    unoptimized: true
  }
}
module.exports = nextConfig

5 Setup eslint

**/node_modules/*
**/out/*
**/.next/*
**/next-env.d.ts
**/next.config.js
**/docs/*
**/storybook/*
**/storybook-static/*
{
  "env": {
    "browser": true,
    "es2021": true
  },
  "extends": [
    "plugin:react/recommended",
    "standard-with-typescript",
    "prettier", //<-- add this
    "plugin:@next/next/recommended" //<-- add this
  ],
  "overrides": [],
  "parserOptions": {
    "project": ["tsconfig.json"], //<-- add this
    "ecmaVersion": "latest",
    "sourceType": "module"
  },
  "plugins": [
    "react",
    "prettier" //<-- add this
  ],
  "rules": {
    "prettier/prettier": ["error"], //<-- addthis
    "@typescript-eslint/no-misused-promises": ["error", { "checksVoidReturn": false }], //<-- add this
    "react/prop-types": "off", // <-- add this
    "react/react-in-jsx-scope": "off", // <-- add this
    "react/jsx-props-no-spreading": "off" // <-- add this
  }
}

5.1 Run eslint and prettier

npm run lint

In package.json add to scripts "prettier": "prettier --write ."

npm run prettier
  • Ctrl+Shift+P > ESLint: Fix all auto-fixable problems
    • You have to do this for each page

5.2 Typical lint errors

  • PROBLEM: Nextjs page return type not given
    • SOLUTION: Add any type
import { NextPage } from 'next'
export default function Home (): any { ....

6 File structure determines routing

Page filename in nextjs like “landingpage.js” must be lowercase letters
React Component filename in nextjs must be Uppercase like “MyCustomButton”.

Modules will

7 Next auth

npm install --save next-auth

8 How do you select SSG, PWA, SSR

Notice how the lines are actually blurred. You can mix and match so the site behaves like an PWA or a static site.

9 Capturing the [carID] and [modelID] as variables

import { useRouter } from 'next/router'

export default function Home() {
    const router = useRouter();
    const {myrouteINPUT} = router.query;
    return(<div>
        <h1>hi the text in the url parameter is {myrouteINPUT}
        </h1>
    </div>)
}

10 ServerSide Render

  1. getStaticPath prerenders dynamic routes, basically you tell [car].js to use {toyota, hyundai} then we prebuild toyota.html hyundai.html
  2. getStaticProps fetches data on build when next build is called. It only runs on server-side.
    The client or window will never see any network call.
    This is NOT like window.addEventlistener('onload',..) that fetches data on each client request.

10.1 2 types of SSG

getStaticProps() getStaticPaths()
calls on build calls on first request

10.2 Why SSG can be bad?

SSG at build time and SSG at first request both are affected by stale data.

  • If db changes once, SSG at build time is already stale
  • If db changes more than once, SSG at first request becomes stale This means if more than one update in db we have to rebuild the ENTIRE site.

10.3 Solution to Stale SSG - ISR

  • Solution ISR which basically means the specific static page ONLY will be rebuilt,
  • If your clientside code auto refreshes every 10 sec, the data will be relatively current.

11 Self is Not Defined

Reason for error: Pixi library uses ‘self’ or ‘window’ which is only defined in the client side. NextJS does prerendering always on the serverside meaning it will run Pixi on the serverside where there is no ‘self’ or ‘window’.

Fix:

  1. Use import dynamic from 'next/dynamic';

  2. Use useEffect because it only runs on client side

12 Exporting as static site

modify package.json for "export": "next export"

npm run build
npm run export

build will create an .next file that is only useful for servers
export will create an out file containing static files like index.html
All we need are the out files

Errors:

  1. imageOptimisation only works with a server which we dont have. Fis with the experimental turn off image optimization option.
  2. Paths are by default relative. Nextjs believes http://server.opnroot.com:5500/ is our root. We need to modify our paths with assetPrefix and basePath so it points to http://server.opnroot.com:5500/test1/out as root.
    • apparently assetPrefix is js files and css files while basePath is for images and route links
/** @type {import('next').NextConfig} */
const { PHASE_DEVELOPMENT_SERVER } = require('next/constants')

let isProd = process.env.myEnv === "prod";
module.exports = (phase, { defaultConfig }) => {
  if (phase === PHASE_DEVELOPMENT_SERVER){ isProd = false };  
  return {
    /* config options for all phases except development here */
    reactStrictMode: true,
    assetPrefix: isProd ? "/MathematicaDocNetwork" : undefined, //static assets are in the /out folder
    basePath: isProd ? "/MathematicaDocNetwork" : undefined,
    experimental: {
      images: {
        unoptimized: true,
      },
    }
  }
}

12.1 Images

Also note that referring to images in *.jsx must be /vercel.svg, NOT ./vercel.svg or vercel.svg.
Example: <Image src={"/vercel.svg"} width={50} height={50}></Image>

12.2 Dynamic Routes DONT work in SSG

  • Dynamic routes don’t really work but the build still runs
    • getStaticPaths() is used to pregenerate the routes which defeats the whole purpose of “Dynamic” routes.
import Head from 'next/head'
import { useRouter } from 'next/router' 


export default function Home(props) {
    const {BLEHdata} = props // This is data loaded from getStaticProps
    console.log(BLEHdata)


    const router = useRouter();
    const {myrouteINPUT} = router.query; 
    //NOTICE `myrouteINPUT` is undefined, 
    //since exported static site cant capture dynamic routes
    return(<div>
        <h1>hi
         {myrouteINPUT}
        </h1>
    </div>)
}

//this pregenerates the "dynamic route" for [car].js
//REQUIRED FOR ANY DYNAMIC ROUTE that gets exported as static site.
//clearly this isnt really that dynamic since we have to hardcode this.
export async function getStaticPaths(){
    return{
        paths: [{ params : {car: "1"}}],
        fallback: true,
    }
}

//This is NOT like just calling window.addEventlistener("onload",...)
//This runs on build time, meaning the browser will NEVER see the GET request to the json server.
export async function getStaticProps(){
    const response =await fetch("http://jsonplaceholder.typicode.com/users")
    const BLEHdata = await response.json()
    console.log(BLEHdata) //this will not output on browser but in terminal
    return{
        props: {BLEHdata}
    }
}

13 ISR

interface IServerData {
  serverData: any;
}
export default function Home({ serverData }: IServerData): any {...}

export async function getStaticProps(): Promise<any> {
  const getData = async (
    url = "https://api.github.com/repos/userJY/someRepo/contents/ooout.json"
  ): Promise<any> => {
    // Default options are marked with *
    const response = await fetch(url, {
      method: "GET", // *GET, POST, PUT, DELETE, etc.
      mode: "cors", // no-cors, *cors, same-origin
      cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
      credentials: "same-origin", // include, *same-origin, omit
      headers: {
        Authorization:
          "token github_pat_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
        Accept: "application/vnd.github.v3.raw",
        "Content-Type": "application/json",
        // 'Content-Type': 'application/x-www-form-urlencoded',
      },
      redirect: "follow", // manual, *follow, error
      referrerPolicy: "no-referrer", // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
    });
    return await response.json(); // parses JSON response into native JavaScript objects
  };

  const serverData = await getData();
  // console.log("terminal:[size]", BLEHdata); // this will not output on browser but in terminal
  return {
    props: { serverData },
    revalidate: 100,
  };
}