--- title: React Next.js project guide tags: Projects --- # React Next.js project guide ## Next.js * [Next.js](https://github.com/vercel/next.js) * [chakra](https://chakra-ui.com/) ### Install Next.js [with chakra-ui and TypeScript](https://github.com/vercel/next.js/tree/canary/examples/with-chakra-ui-typescript) * `npx create-next-app [name] --example with-chakra-ui-typescript` ### Install ESLint and Prettier * `npm i -D @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint eslint-config-prettier eslint-plugin-react prettier` ### Configire Prettier * add **.prettierrc** ```json=1 { "semi": true, "trailingComma": "none", "singleQuote": true, "printWidth": 80, "tabWidth": 2 } ``` ### Configire ESLint * add **.eslintrc** ```json= { "env": { "browser": true, "es6": true, "node": true }, "plugins": ["react", "@typescript-eslint"], "extends": [ "eslint:recommended", "plugin:react/recommended", "next/core-web-vitals", "plugin:@typescript-eslint/eslint-recommended", "prettier" ], "globals": { "Atomics": "readonly", "SharedArrayBuffer": "readonly" }, "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaFeatures": { "jsx": true }, "ecmaVersion": 2020, "sourceType": "module" }, "settings": { "react": { "version": "detect" } }, "rules": { /***************************************** * Eslint Rules * DOC: https://eslint.org/docs/rules ****************************************/ "no-console": "off", // suppress errors for missing 'import React' in files "react/react-in-jsx-scope": "off", // React component names must start with an uppercase letter. "react-hooks/rules-of-hooks": "off", /*********************************** * other plugins **********************************/ "@typescript-eslint/no-explicit-any": "off" } } ``` ### Configure project settings * add **.vscode/settings.json** ```json= { // Editor "editor.codeActionsOnSave": { "source.organizeImports": true, "source.fixAll.eslint": true }, "editor.defaultFormatter": "esbenp.prettier-vscode", "[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "cSpell.words": [ "Chakra" ] } ``` ### Configure i18n * `npm i next-i18next` * [document](https://github.com/isaachinman/next-i18next) * add **next.config.js** ```javascript= const { i18n } = require('./next-i18next.config'); module.exports = { i18n, ... }; ``` * add **next-i18next.config.js** ```javascript= module.exports = { i18n: { defaultLocale: 'en', locales: ['en', 'zh-TW'], localePath: './src/i18n/locales' } }; ``` * add configuration in **_app.tsx** ```typescript= import { appWithTranslation } from 'next-i18next'; const MyApp = ({ Component, pageProps }) => <Component {...pageProps} />; export default appWithTranslation(MyApp); ``` * add script in **package.json** ```json= { ... "scripts": { ... "locales": "npx prettier --write ./src/i18n/**/*.json" } ... } ``` * add **locales .sh** ```shell= # Remember to set script in your package.json # npx prettier --write ./src/i18n/**/*.json # Set the script name in your package.json NPMSCRIPT=locales # Set path and folder names BASE=src/i18n TSFOLDER=localests FOLDER=locales SOURCE=$BASE/$TSFOLDER/* DESTINATION=$BASE/$FOLDER # Remove old files if exist rm -r $DESTINATION # Generate locales folder with only files we need rsync -zarvh $SOURCE $DESTINATION --exclude @types # Point to the BASE folder cd $BASE # Loop through DESTINATION folder for dir in $FOLDER/*; do ( cd "$dir" for FILE in *; do ( # echo $FILE; # remove everything after "import", everything before "=" # remove everything after ";" (deal with ";" being exist in the end) sed -i 's/import.*\|.*=//g;s/;.*//g' $FILE; mv -- "$FILE" "${FILE%.ts}.json"; ) done ) done # Format npm run $NPMSCRIPT # NOTE: # if you excecute "npx prettier --write ./src/i18n/**/*.json" directly here in this script # It will call "$PATH" varible which store in global # The $PATH will be something like this # "/home/yourpcname/.nvm/versions/node/v14.17.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin" # Hence you can't point to "./src/i18n/**/*.json" successfully ``` * The folder will look like this ![](https://i.imgur.com/WMs43Lg.png) * Alternative JavaScript version of that **locales .sh** [Detail](https://hackmd.io/PnuZcIojRi-WURxHZqAVdw) ## Snippets * Open VScode -> File -> Preferences -> User Snippets * Type ==typescriptreact.json== * Paste!! ```json=1 { // Place your snippets for typescriptreact here. Each snippet is defined under a snippet name and has a prefix, body and // description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are: // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the // same ids are connected. // Example: "Typescript React PureComponent": { "prefix": "rpc", "body": [ "import * as React from 'react'", "", "export class $1 extends React.PureComponent {", "\trender() {", "\t\treturn ($2);", "}}" ], "description": "Typescript React PureComponent" }, "Twind": { "prefix": "cc", "body": ["className={tw`$1`}"], "description": "Twind" }, "Typescript React Function Component": { "prefix": "rh", "body": [ "import React from 'react'", "", "type ${TM_FILENAME_BASE}Props = {", "$1", "}", "", "const $TM_FILENAME_BASE: React.FC<${TM_FILENAME_BASE}Props> = ({$2}:${TM_FILENAME_BASE}Props) => {", "\t\treturn (<div>${TM_FILENAME_BASE} page!</div>);", "}", "", "export default ${TM_FILENAME_BASE}" ], "description": "Typescript React Function Component" }, "React Native StyleSheet": { "prefix": "rnss", "body": [ "import {StyleSheet} from 'react-native'", "const styles = StyleSheet.create({", "", "});" ], "description": "React Native StyleSheet" }, "Toggle State": { "prefix": "tog", "body": ["this.setState(state => ({", "\topen: !state.open", "}));"], "description": "toggle state" }, "console.log": { "prefix": "cl", "body": ["console.log($1)"], "description": "console.log" }, "className={classnames()}": { "prefix": "cc", "body": ["className={classnames('$1')}"], "description": "tailwind react stuff" }, "Apollo Query Component": { "prefix": "apq", "body": [ "interface Props {", " children: (data: QueryResult<$1, OperationVariables>) => JSX.Element;", "}", "", "export class $2 extends React.PureComponent<Props> {", " render() {", " return (", " <Query<$1> query={$3}>{x => this.props.children(x)}</Query>", " );", " }", "}" ], "description": "Apollo Query Component" } } ``` ## Debug * `You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.` * **solution:** * 1. `npm i next-images` * 2. Modify ==next.config.js== ```javascript= const { i18n } = require('./next-i18next.config'); const withImages = require('next-images'); module.exports = withImages({ i18n }); ``` * 3. add ==@types\images.d.ts== in your root path ```typescript= declare module '*.jpg'; declare module '*.jpeg'; declare module '*.png'; ``` * 4.Modify ==tsconfig.json== ```json= { "compilerOptions": { ... "typeRoots": ["node_modules/@types", "src/@types"] }, ... } ``` ## Animation ```typescript= <ScaleFade key={router.route} initialScale={0.98} in={true} transition={{ enter: { duration: 1, delay: 0.2 } }} > <Box minH="70vh" maxW={maxW} w="100%"> {children} </Box> </ScaleFade> ``` --- ## Update history ==Newer on the top== | Date | Author | Description | |:---------- |:------:|:--------------- | | 11/02/2022 | Alice | update | | 27/01/2022 | Alice | update | | 04/06/2021 | Alice | update | | 15/05/2021 | Alice | initial version |