Currently Lovia NextJS stack uses:
- MUI v5 (emotion) with NextJS-SSR (no Capacitor), for SEO. TypeScript + Storybook + Jest + SWR + Axios + urql + Cypress + Prettier + ESLint.
- Reference: https://itnext.io/nextjs-storybookjs-material-ui-jest-swr-fe2ff5cb9af8
- Use React Router instead of NextJS routing
- You can use NextJS built-in routing instead of React Router v6, it is easier but not as flexible
- MUI v5 with NextJS-CSR & Capacitor
- Admin templates for MUI: Berry Free
- React Starter Kit
- React Hook Form (with Controller for MUI components)
- ajv
In cases where mobile app is needed, we use Lovia Progressive Web App (PWA) stack which consists of:
- React Web
- TypeScript
- MUI v5
- Minimal UI Kit – both Minimal Free and Pro version
- Axios for REST API calls, and if REST API publishes an OpenAPI schema: OpenAPI Generator
- urql
- React Hook Form (with Controller for MUI components)
- ajv
The previous version of the stack uses:
- React Web
- TypeScript
- Ant Design Pro v5 which uses UmiJS v3
- Axios for REST API calls, and if REST API publishes an OpenAPI schema: OpenAPI Generator
- Apollo Client for GraphQL
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:
- https://medium.com/opensanca/migrating-from-js-to-ts-cra-b5f679086c5a
- https://prettier.io/docs/en/integrating-with-linters.html
- https://github.com/prettier/eslint-config-prettier
- https://dev.to/saurabhggc/add-eslint-prettier-and-airbnb-to-your-project-3mo8
- https://create-react-app.dev/docs/adding-typescript/
- https://www.jbssolutions.com/resources/blog/creating-react-app-scratch-webpack5-typescript4-react17-part-1/
Troubleshooting
src/components/authentication/login/LoginForm.tsx
Line 61:61: Parsing error: Unexpected token, expected “,” (61:61)
Conclusion:
- 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.
- 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