Engineering

⌘K
  1. Home
  2. Docs
  3. Engineering
  4. React Progressive Web App and NextJS

React Progressive Web App and NextJS

Currently Lovia NextJS stack uses:

In cases where mobile app is needed, we use Lovia Progressive Web App (PWA) stack which consists of:

The previous version of the stack uses:

Converting CRA JavaScript into TypeScript

Dependencies:

yarn add typescript @types/react @types/react-dom @types/node @types/jest
yarn add --dev @babel/preset-typescript
# As of Oct 17, 2021, react-scripts CRA is not compatible yet with eslint 8+
yarn add --dev eslint@7 @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-config-prettier@7

Delete jsconfig.json file.

Rename App.jsx into App.tsx. import React from ‘react’. Then yarn start.

In tsconfig.json, set “jsx”: “react-jsx”. Reason: compilerOptions.jsx must be react-jsx (to support the new JSX transform in React 17)

Then, add "prettier" to the “extends” array in your .eslintrc.* file. Make sure to put it last, so it gets the chance to override other configs.

Reference:

Troubleshooting

src/components/authentication/login/LoginForm.tsx
Line 61:61: Parsing error: Unexpected token, expected “,” (61:61)

Conclusion:

  1. During migration with mixed JS-TS, in .eslintrc you can use @babel/eslint-parser. Don’t convert all JS files at once, you may get a lot of “as any”, etc.
  2. After you have migrated all files to TS, change .eslintrc to use @typescript-eslint/parser.

In package.json:

  "scripts": {
    ...
    "lint": "eslint --ext .js,.jsx,.ts,.tsx ./src",
    "lint:fix": "eslint --fix --ext .js,.jsx,.ts,.tsx ./src",
    "format": "prettier -w ."
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "babel": {
    "presets": [
      "@babel/preset-env",
      "@babel/preset-react",
      "@babel/preset-typescript"
    ],
    "plugins": [
      "@babel/plugin-proposal-nullish-coalescing-operator",
      "@babel/plugin-proposal-optional-chaining"
    ]
  },

tsconfig.json: (this is not used by babel but by typescript type checker)

{
  "compilerOptions": {
    "baseUrl": "src/",
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "sourceMap": true
  },
  "include": ["src"],
  "exclude": ["**/*.test.ts", "**/*.test.tsx"]
}

Sample .eslintrc:

{
  "root": true,
  "env": {
    "browser": true,
    "node": true,
    "es2021": true
  },
  "extends": [
    "airbnb",
    "plugin:jsx-a11y/recommended",
    "plugin:react-hooks/recommended",
    "plugin:@typescript-eslint/eslint-recommended",
    "plugin:@typescript-eslint/recommended",
    "prettier"
  ],
  "plugins": ["react", "@typescript-eslint", "react-hooks"],
  "parser": "@babel/eslint-parser",
  // "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaVersion": 12,
    "requireConfigFile": false,
    "ecmaFeatures": {
      "experimentalObjectRestSpread": true,
      "impliedStrict": true,
      "jsx": true
    },
    "sourceType": "module"
  },
  "rules": {
    "import": 0,
    "max-len": 0,
    "no-alert": 0,
    "no-shadow": 0,
    "no-console": 0,
    "comma-dangle": 0,
    "import/no-cycle": 0,
    "react/prop-types": 1,
    "no-return-assign": 0,
    "consistent-return": 1,
    "no-param-reassign": 0,
    "react/display-name": 0,
    "no-use-before-define": 0,
    "no-underscore-dangle": 0,
    "react/button-has-type": 1,
    "react/no-children-prop": 0,
    "react/forbid-prop-types": 0,
    "jsx-a11y/anchor-is-valid": 0,
    "react/react-in-jsx-scope": 0,
    "react/no-array-index-key": 0,
    "react/no-unused-prop-types": 1,
    "react/require-default-props": 0,
    "react/no-unescaped-entities": 0,
    "import/prefer-default-export": 0,
    "react/jsx-props-no-spreading": 0,
    "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx", ".tsx"] }],
    "react/destructuring-assignment": 0,
    "import/no-extraneous-dependencies": 0,
    "react/jsx-key": 1,
    "react-hooks/rules-of-hooks": 2,
    "no-unused-vars": [
      1,
      {
        "ignoreRestSiblings": false
      }
    ],
    /* temporary by Hendy */
    "react/jsx-wrap-multilines": 1,
    "react/jsx-one-expression-per-line": 1,
    "import/extensions": 1
  },
  "settings": {
    "import/resolver": {
      "node": {
        "extensions": [".js", ".jsx", ".ts", ".tsx"],
        "paths": ["src"]
      }
    }
  }
}

Worth to Try #1: Change everything to .ts / .tsx then switch the eslint to typescript instead of babel es-parser

You will get a lot of “no implicit any”, but that is only from the ts type checker, ignored by Babel.

Worth to Try #2: See this StackOverflow answer

See https://stackoverflow.com/a/67685854/122441

How to Set Up Redux Toolkit

yarn add redux @reduxjs/toolkit react-redux @types/react-redux

Reference: https://redux-toolkit.js.org/tutorials/typescript

Articles

How can we help?