--- title: 'usthing web tutorial' disqus: hackmd --- # USTHING WEB Tutorial #### Refresh your memory here === [TOC] ## Syllabus ### 0.Env Setup ### 1.Basic Typescript ###### -- Combine Json and Typescript ### 2.Basic React ###### -- Combine React and Typescript #### 3.Basic Html / Css #### 4.Basic Javascript #### 5.Basic Next.js #### 6.Api Calling #### 7.Practises #### 8.Basic Bootstrap (optional frontend lib) ## Env Setup Whenever you using a new system/setup(ios/windows,linux),you need to generate ssh key-pair on that machine and put the public key on github in order to clone or do whatever manipulation to repos on github ### ssh-key #### this is for when you are using the default key path and name ### steps: #### 1. ssh-keygen #### 2. eval $(ssh-agent) #### 3. ssh-add ~/.ssh/id_rsa #### 4. cat ~/.ssh/id_rsa.pub or pbcopy < ~/.ssh/id_rsa.pub #### 5. add the public key on your github settings -> ssh and gpg keys > Read more about env and ssh key setup here: https://support.atlassian.com/bitbucket-cloud/docs/set-up-an-ssh-key/ ### Vscode Extensions (suggest) ![](https://i.imgur.com/f27UaYk.png) ![](https://i.imgur.com/5tvKXgh.png) ![](https://i.imgur.com/yBVEHmI.png) ### Package Manager dont mix up your package managers in one repo , or you will see yarn.lock or package.lock file ##### install yarn (suggest) npm install --global yarn ##### install package with yarn yarn add <package_name> ##### remove package with yarn yarn remove <package_name> > Read more about yarn commands here: https://classic.yarnpkg.com/lang/en/docs/cli/remove/ or > Read more about npm commands here: https://docs.npmjs.com/cli/v7/commands/npm ## Basic Typescript (JavaScript With Syntax For Type.) tsx files for both js/ts(typescript) and html/css ts files for js/ts only ### Basic Typescript Skills ```typescript // Primitive Types string number boolean enum Symbol // array of strings / numbers .. string[] / number[] ... // union => type string or number string | number .. //string "one" or "two" is accepted "one" | "two" // readonly -> cannot change type TustStudent = { readonly name:string age:number; courses:string[]; single:boolean; } // declare Interface // I for Interface (convention) interface IustStudentInfo { name:string; age:number; } // extend interface interface IustStudent extends IustStudentInfo{ // name:string => inherited // age:number courses:string[]; single:boolean; } ``` ### Typescript with Json ```typescript= const student:TustStudent ={ name:'John Doe', age:22, courses:['elec 2100','comp2011'], single:true } const students: IustStudent[] = [ student, { name: "john", age: 10, courses: [], single: false, }, ]; // inline const prof : {name:string , age:number} ={ name:"Prof Yip" , age:56 } ``` ### Utility Types ```typescript async function test () :Promise<string>{ return "nice" } const awaitedFn :Awaited<Promise<string>>= await test() type Ttest ={ time:string value:number dates:string[] } type Tevents = "open" | "close" //set all fields to optional type TpartialTest = Partial<Ttest> //pick fields from types type TtimeOnly = Pick<Ttest, "time"> //omit fields type TnameValue =Omit<Ttest ,"dates"> //type mappings const events:Record<Tevents ,Ttest> ={ open:{ time :"10:00", value:5, dates:["2022-12-11"] }, close:{ time:"12:00", value:10, dates:["2012-12-12"] } } type Topen = Exclude<Tevents, "close"> type Topen2 = Extract<Tevents ,"open"> type TtestReturn = ReturnType<typeof test> type TEvents = Capitalize<Tevents> // "Open" | "Close" //explore more by yourself.... ``` ### Generics ```typescript= //generic Fn function firstElement<Type>(arr: Type[]): Type | undefined { return arr[0]; } // s is of type 'string' const s = firstElement(["a", "b", "c"]); // n is of type 'number' const n = firstElement([1, 2, 3]); // u is of type undefined const u = firstElement([]); //generic Types type Tperson<T> ={ name :string sex:T } const Bob :Tperson<'male'> = props?.person ``` ### Conditional Types ```typescript= type ExtractFunctionReturn<T> = T extends (...args: any[]) => infer R ? R : never; function stringReturner() { return "A String"; } type FunctionReturnType = ExtractFunctionReturn<typeof stringReturner>; ``` ### Indexed Access Types / Template Literal Types / Mapped Types ```typescript= //Indexed Access Types type Person = { age: number; name: string; alive: boolean }; type I2 = Person[keyof Person]; const MyArray = [ { name: "Alice", age: 15 }, { name: "Bob", age: 23 }, { name: "Eve", age: 38 }, ]; type Age = typeof MyArray[number]["age"]; // typeof MyArray["age"] //Mapped Types type OptionsFlags<Type> = { [Property in keyof Type]: boolean; }; type FeatureFlags = { darkMode: () => void; newUserProfile: () => void; }; type FeatureOptions = OptionsFlags<FeatureFlags>; // type FeatureOptions = { // darkMode: boolean; // newUserProfile: boolean; // } //Template Literal Types type EmailLocaleIDs = "welcome_email" | "email_heading"; type FooterLocaleIDs = "footer_title" | "footer_sendoff"; type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`; // type AllLocaleIDs = "welcome_email_id" | "email_heading_id" | "footer_title_id" | "footer_sendoff_id" ``` ### Advanced Mapped Types creating new object types based on existing ones but with modified property names.(using the as clause) ```typescript= type MappedTypeWithAs<T> = { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K] }; interface Person { name: string; age: number; location: string; } type LazyPerson = MappedTypeWithAs<Person>; // Type is: // { // getName: () => string; // getAge: () => number; // getLocation: () => string; // } ``` ### Variadic Tuple Types forwarding parameters and manipulating arrays in a type-safe manner. ```typescript= type StringNumberBooleans = [string, number, ...boolean[]]; const example: StringNumberBooleans = ["hello", 42, true, false, true]; ``` ### Labeled Tuple Elements provide labels to elements in tuple types. ```typescript= type Address = [street: string, city: string, state: string, postalCode: number]; function printAddress(...address: Address) { console.log(`Street: ${address[0]}, City: ${address[1]}, State: ${address[2]}, Postal Code: ${address[3]}`); } ``` ### Type Guards narrow types based on conditions. ```typescript= function isString(test: any): test is string { return typeof test === "string"; } function example(foo: any) { if (isString(foo)) { console.log(foo.toUpperCase()); // foo is narrowed to string } } ``` ### Assertion Functions function that perform runtime checks and throw an error if a condition is not me ```typescript= function assertIsNumber(val: any): asserts val is number { if (typeof val !== "number") { throw new AssertionError("Not a number!"); } } function multiply(x: any, y: any) { assertIsNumber(x); assertIsNumber(y); return x * y; // x and y are narrowed to numbers } ``` ### Recursive Type Aliases types that refer to themselves in a recursive manner. This is particularly useful for modeling data with a hierarchical or recursive structure, such as trees or linked lists. ```typescript= type JsonValue = string | number | boolean | null | JsonArray | JsonObject; interface JsonArray extends Array<JsonValue> {} interface JsonObject { [key: string]: JsonValue; } ``` ### Self Challenge Q1: how does type AdvancedConfig look like? (Generics and Conditional Types) ```typescript= type EventConfig<Events extends { kind: string, payload?: any }> = { [E in Events as `${E["kind"]}Handler`]: E['payload'] extends undefined ? (event: E) => void : (event: E & { timestamp: Date }) => void; } type SquareEvent = { kind: "square", x: number, y: number }; type CircleEvent = { kind: "circle", radius: number, payload: { area: number } }; type AdvancedConfig = EventConfig<SquareEvent | CircleEvent>; // Usage would look like: const config: AdvancedConfig = { squareHandler: (event) => console.log(event.x), circleHandler: (event) => console.log(event.payload.area, event.timestamp) // Note additional timestamp property }; ``` Q2: how does type GDPRCompliantFields look like? (Enhanced PII Data Extraction with Meta Programming) ```typescript= type EnhancedExtractPII<Type> = { [Property in keyof Type]: Type[Property] extends { pii: true } ? { encrypted: boolean, value: string } // Transform the type : Type[Property]; }; type DBFields = { id: { format: "incrementing" }; name: { type: string; pii: true }; age: number; }; type GDPRCompliantFields = EnhancedExtractPII<DBFields>; // The type GDPRCompliantFields will have the name transformed to include encryption info, // while other properties remain unchanged. ``` Q3: Create a type that maps HTTP methods to TypeScript types representing the payload structure required for requests and responses. ```typescript= type HttpMethods = "GET" | "POST" | "PUT" | "DELETE"; type RequestPayloads = { GET: never; // GET does not have a payload POST: { data: any }; PUT: { id: number; changes: any }; DELETE: { id: number }; }; type ResponseTypes = { GET: any[]; POST: { success: boolean; id: number }; PUT: { updated: boolean }; DELETE: { deleted: boolean }; }; type ApiRouteConfig<Method extends HttpMethods> = { method: Method; handleRequest: (payload: RequestPayloads[Method]) => ResponseTypes[Method]; }; // Usage would look like: const postRoute: ApiRouteConfig<"POST"> = { method: "POST", handleRequest: (payload) => ({ success: true, id: 123 }) }; ``` Q4: Develop a system where you create type-safe component props definitions using a mapping from prop names to types, which automatically generates optional and required versions of props based on an array of required field names. ```typescript= type ComponentProps<T, RequiredKeys extends keyof T = never> = { [P in keyof T as P extends RequiredKeys ? P : never]-?: T[P]; } & { [P in keyof T as P extends RequiredKeys ? never : P]+?: T[P]; } type ButtonProps = { onClick: () => void; label: string; size?: 'small' | 'medium' | 'large'; } type SafeButtonProps = ComponentProps<ButtonProps, 'onClick' | 'label'>; // A button instance would need to have onClick and label, but size is optional. const buttonProps: SafeButtonProps = { onClick: () => console.log("Clicked!"), label: "Submit", size: "large" // This is optional }; ``` >check https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates and https://www.typescriptlang.org/docs/handbook/2/classes.html as well ### Summary #### IustStudent similar to TustSutdent Difference between Type and Interface: https://blog.logrocket.com/types-vs-interfaces-in-typescript/#:~:text=Interfaces%20are%20basically%20a%20way%20to%20describe%20data,union%2C%20primitive%2C%20intersection%2C%20tuple%2C%20or%20any%20other%20type. #### Why use typescript It implements type system for javascript and detect type errors at dev time 70 - 80 % of run time errors come from type errors https://stackoverflow.com/questions/12694530/what-is-typescript-and-why-would-i-use-it-in-place-of-javascript #### Add export in front to use in any file ```typescript export type Demo ={ name:string } ``` > Read more about Typescript here: https://www.typescriptlang.org/docs/handbook/2/basic-types.html ## Basic React Hooks(v18.3.1) ### useState state : a variable in React | setState : setter function to inform react that u want to change the variable ```typescript const [state,setState] = useState<number | string[] | boolean| IustStudent>() ``` ### useEffect where you run your functions when something changed ##### [] for running only once on first render ```typescript useEffect(() => { // execute your function or even write your function here return () => { // clean up your useEffect hook here } }, [dependencies]) ``` #### Example ```typescript const [userName,setUserName] = useState<string>('') const [loggedIn,setLoggedIn] = useState<boolean>(false) useEffect(() => { let mount = true const logMeIn = () => { setUserName(user?.name) setLoggedIn(true) } if(mount && user)logMeIn() return () => { mount = false } }, [user]) ``` #### Example Explanation Assume user is an object returned by firebase library, >When user changed from null to something, we need to update our loggedIn state and userName so this react hook will be monitoring the user object and declare logMeIn and run it when user object change #### Advanced concept Why you need to clean up? >when you switch to another page ,your function in useEffect might still be running so we change mount = false when we switch to another page , and use mount as the condition to run the function this prevents memory leak (slow performance) Memory Leak Details https://auth0.com/blog/four-types-of-leaks-in-your-javascript-code-and-how-to-get-rid-of-them/ ### useCallback returns a memoized version of the callback that only changes if one of the dependencies has changed. Useful when passing callbacks to optimized child components. #### Example ```typescript import { useCallback, useState } from 'react'; function Counter() { const [count, setCount] = useState(0); const increment = useCallback(() => { setCount(c => c + 1); }, []); // Dependencies list return <button onClick={increment}>Count: {count}</button>; } ``` ### useMemo is used to memoize expensive calculations. If the dependencies haven't changed since the last render, React reuses the memoized value instead of recalculating it. #### Example ```typescript import { useMemo } from 'react'; function ExpensiveComponent({ items }) { const sortedItems = useMemo(() => { return items.sort((a, b) => a.value - b.value); }, [items]); // Dependency array return <div>{sortedItems.map(item => <div key={item.id}>{item.value}</div>)}</div>; } ``` ### useReducer is an alternative to useState, ideal for managing more complex state logic in components. #### Example ```typescript import { useReducer } from 'react'; const initialState = {count: 0}; function reducer(state, action) { switch (action.type) { case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; default: throw new Error(); } } function Counter() { const [state, dispatch] = useReducer(reducer, initialState); return ( <> Count: {state.count} <button onClick={() => dispatch({type: 'increment'})}>+</button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> </> ); } ``` ### useContext lets you subscribe to React context without introducing nesting. #### Example ```typescript import React, { useContext, createContext } from 'react'; const ThemeContext = createContext('light'); function ThemedButton() { const theme = useContext(ThemeContext); return <button style={{ background: theme === 'dark' ? 'black' : 'white' }}>Click me</button>; } ``` ### useRef returns a mutable ref object whose .current property is initialized with the passed argument. The returned object will persist for the full lifetime of the component. #### Example ```typescript import { useRef } from 'react'; function TextInputWithFocusButton() { const inputEl = useRef(null); const onButtonClick = () => { inputEl.current.focus(); }; return ( <> <input ref={inputEl} type="text" /> <button onClick={onButtonClick}>Focus the input</button> </> ); } ``` ### useTransition helps keep your app responsive during state updates that might cause heavy re-renders. #### Example ```typescript import { useTransition, useState } from 'react'; function FilterComponent({ items }) { const [input, setInput] = useState(''); const [isPending, startTransition] = useTransition(); const handleChange = (e) => { setInput(e.target.value); startTransition(() => { // Perform a heavy filtering operation }); }; return ( <div> <input type="text" value={input} onChange={handleChange} /> {isPending ? 'Updating...' : items.map(item => <div key={item.id}>{item.name}</div>)} </div> ); } ``` ### useDeferredValue This hook is used to defer the value used for an expensive render to allow the UI to remain responsive. #### Example ```typescript import { useDeferredValue, useState } from 'react'; function SearchResults({ searchTerm }) { const deferredSearchTerm = useDeferredValue(searchTerm); const results = performSearch(deferredSearchTerm); // Some search function return ( <ul> {results.map(result => ( <li key={result.id}>{result.title}</li> ))} </ul> ); } ``` ### useActionState Call useActionState at the top level of your component to access the return value of an action from the last time a form was submitted. #### Example ```typescript import { useActionState } from "react"; async function increment(previousState, formData) { return previousState + 1; } function StatefulForm({}) { const [state, formAction] = useActionState(increment, 0); return ( <form> {state} <button formAction={formAction}>Increment</button> </form> ) } ``` The form state is the value returned by the action when the form was last submitted. If the form has not yet been submitted, it is the initial state that you pass. If used with a Server Action, useActionState allows the server’s response from submitting the form to be shown even before hydration has completed. ### useFormStatus is a Hook that gives you status information of the last form submission. #### Example ```typescript import { useFormStatus } from "react-dom"; import action from './actions'; function Submit() { const status = useFormStatus(); return <button disabled={status.pending}>Submit</button> } export default function App() { return ( <form action={action}> <Submit /> </form> ); } ``` #### pitfall: useFormStatus will not return status information for a <form> rendered in the same component. The useFormStatus Hook only returns status information for a parent <form> and not for any <form> rendered in the same component calling the Hook, or child components. ```typescript function Form() { // 🚩 `pending` will never be true // useFormStatus does not track the form rendered in this component const { pending } = useFormStatus(); return <form action={submit}></form>; } ``` Instead call useFormStatus from inside a component that is located inside <form>. ```typescript function Submit() { // ✅ `pending` will be derived from the form that wraps the Submit component const { pending } = useFormStatus(); return <button disabled={pending}>...</button>; } function Form() { // This is the <form> `useFormStatus` tracks return ( <form action={submit}> <Submit /> </form> ); } ``` ## React API(v18.3.1) ### memo memo is a higher order component for memoizing a component. ```typescript import React, { memo } from 'react'; const MyComponent = memo(function MyComponent(props) { /* render using props */ }); ``` ### createContext This API creates a Context object. When React renders a component that subscribes to this Context object, it will read the current context value from the closest matching Provider above it in the tree. ```typescript import { createContext } from 'react'; const MyContext = createContext(defaultValue); ``` ### forwardRef is used to pass refs down to child components. ```typescript import React, { forwardRef } from 'react'; const FancyButton = forwardRef((props, ref) => ( <button ref={ref} className="FancyButton"> {props.children} </button> )); // You can now get a ref directly to the DOM button: const ref = React.createRef(); <FancyButton ref={ref}>Click me!</FancyButton>; ``` ### act is a utility from the React Testing Library that ensures updates related to state changes, subscriptions, and effects are applied before assertions are made in tests. This utility wraps the code that updates state and asserts the results in a way that reflects what happens in a browser more closely. ```typescript import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { act } from 'react-dom/test-utils'; import Counter from './Counter'; // Assume Counter is a component that increments a count state test('increments counter', () => { render(<Counter />); const button = screen.getByRole('button', { name: /increment/i }); act(() => { userEvent.click(button); }); expect(screen.getByText(/count: 1/i)).toBeInTheDocument(); }); ``` ### lazy is a function that allows you to render a dynamic import as a regular component. It is used to dynamically load components only when they are needed, which helps to split the code at a more granular level and reduce the size of the initial payload. ```typescript import React, { Suspense } from 'react'; const LazyComponent = React.lazy(() => import('./LazyComponent')); function App() { return ( <div> <Suspense fallback={<div>Loading...</div>}> <LazyComponent /> </Suspense> </div> ); } ``` ### cache (sever components only) Call cache outside of any components to create a version of the function with caching. ```typescript import {cache} from 'react'; import calculateMetrics from 'lib/metrics'; const getMetrics = cache(calculateMetrics); function Chart({data}) { const report = getMetrics(data); // ... } ``` When getMetrics is first called with data, getMetrics will call calculateMetrics(data) and store the result in cache. If getMetrics is called again with the same data, it will return the cached result instead of calling calculateMetrics(data) again. ### use Call use in your component to read the value of a resource like a Promise or context. ```typescript import { fetchMessage } from './lib.js'; import { Message } from './message.js'; export default function App() { const messagePromise = fetchMessage(); return ( <Suspense fallback={<p>waiting for message...</p>}> <Message messagePromise={messagePromise} /> </Suspense> ); } // message.js 'use client'; import { use } from 'react'; export function Message({ messagePromise }) { const messageContent = use(messagePromise); return <p>Here is the message: {messageContent}</p>; } ``` Because Message is wrapped in Suspense, the fallback will be displayed until the Promise is resolved. When the Promise is resolved, the value will be read by the use API and the Message component will replace the Suspense fallback. #### Pitfall: use cannot be called in a try-catch block. Instead of a try-catch block wrap your component in an Error Boundary, or provide an alternative value to use with the Promise’s .catch method. ### React with Typescript ```typescript // declare state with string const [name ,setName] = useState<string>('') // declare state with array of string const [arr ,setArr] = useState<string[]>([]) // declare state with object type ustStudent const [student,setStudent] =useState<TustStudent | null>(null) // declare state with array of ustStudents const [courseStudents,setCourseStudents] =useState<TustStudent[]| null>(null) // setState type setName:(value:string) => void or setName:(value:typeof name) => void // type with function params // void mean function return void const logStudentInfo = (name:string,age:number):void =>{ console.log('student name',name) } ``` ### React Components and Props ```typescript= function Welcome(props) { return <h1>Hello, {props?.name}</h1>; } function App() { return ( <div> <Welcome name="Sara" /> <Welcome name="Cahal" /> <Welcome name="Edite" /> </div> ); } ``` ### Conditional Rendering ```typescript= const [show,setShow] = useState(false) export const Demo =() =>{ return( <> {show && <p>show is true</p>} {show ? <p>show is true</p> : <p>show is false</p>} </> ) } ``` ### List and Keys ###### Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity: ```typescript= const numbers = [1, 2, 3, 4, 5]; const listItems = numbers.map((number) => <li key={number.toString()}> {number} </li> ); ``` ### Dont ' s Infinte Loops ```typescript= const [state,setState] = useState('') useEffect(() => { setState('one') }, [state]) ``` Map without Keys ```typescript= sth.map(d => ( <div></div> )) ``` #### explanation This useEffect hook will create an infinite loop. Because the hook is monitoring state , but you are changing the state inside the hook , so this action will keep looping ## Basic Html/Css ##### Center a div ```css= display:flex; justify-content:center; align-self:center; ``` ##### Align HTML elements horizontally in a div ```css= display:flex; flex-direction:row; column-gap: 50px; ``` ##### Text and Font related css ``` css= font-size: 5em; text-align: center; font-family: "poppins"; // import from google fonts font-size: 35px; font-weight: bold; color: #3145f5; ``` ##### div structure ``` css= height:50; width:100; border-radius:25px; box-sizing: border-box; ``` ##### Padding and Margin ```css= margin: 10rem; //space outside div padding: 10rem; //space inside div ``` ##### useful properties ```css= display:inline; opacity:0.5; z-index:1; font-size:12px; background-color:white; font-weight:200 ``` > Read more about HTML and CSS here: > https://www.w3schools.com/css/css_rwd_intro.asp > ## Basic Javascript javascript methods is actually faster than any third party function libs(e.g lodash ) ##### Data Structures ```typescript= //Map const map = new Map<string , number>([["apples", 500], ["bananas", 300]); map.set('one' ,1); //inferred type => numbers const num = map.get('one'); //1 const check:boolean = map.has('two'); // false const size= map.size //3 //Set const set:Set<number> = new Set([2,3]); set.add(1) set.has(4) // false ... ``` ##### Array Methods ```typescript= //map const users:{firstName:string , lastName:string}[] = [ {firstName : "Susan", lastName: "Steward"}, {firstName : "Daniel", lastName: "Longbottom"}, {firstName : "Jacob", lastName: "Black"} ]; const userFullnames = users.map(function(element){ return `${element.firstName} ${element.lastName}`; }) //flat const arr1 = [0, 1, 2, [3, 4]]; console.log(arr1.flat()); // expected output: [0, 1, 2, 3, 4] //filter const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present']; const result = words.filter(word => word.length > 6); console.log(result); // expected output: Array ["exuberant", "destruction", "present"] //concat const array1 = ['a', 'b', 'c']; const array2:string[] = ['d', 'e', 'f']; const array3 = array1.concat(array2); console.log(array3); // expected output: Array ["a", "b", "c", "d", "e", "f"] //includes const pets = ['cat', 'dog', 'bat']; console.log(pets.includes('cat')); // expected output: true console.log(pets.includes('at')); // expected output: false ``` > Read more about Javascript Methods here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes #### Advanced skills ```typescript= const num1 =10; const num2 = 20; //ternary form num1 === num2 ? console.log("yes") : console.log('No') //shorthand const nums:{num1:number,num2:number} ={ //instead of num1:num1 .... num1,num2 } //instead of <Component num1={num1} /> <Component {...{num1}}/> // destructuring const {num1} = nums //note : make sure num1 exist in nums in order to use destructuring //if not , use below technique to prevent runtime error const num = nums?.num1 ``` ## Basic Next.js #### Link Component Navigation ```typescript= import Link from 'next/link'; <h1 className="title"> Dont Click Me <Link href="/foo/bar">Click Me</Link> </h1> ``` In Nextjs, you can use Link component instead of `<a href></a>`. ### Server side rendering #### getStaticProps ```typescript= export async function getStaticProps(context) { //You can do whatever function you want, such as calling API. return { props: {}, // will be passed to the page component as props } } ``` Nextjs will pre-render this page **ONLY at build time** with using the props returned by getStaticProps. #### getServerSideProps ```typescript= export async function getServerSideProps(context) { //You can do whatever function you want, such as calling API. return { props: {}, //data will be passed to the page component as props } } ``` Next.js will pre-render the page on **each request** using the data returned by getServerSideProps. ### Routes Next.js has a file-system based router built on the concept of pages. When a file is added to the **/pages** directory, it's automatically available as a route. The dynamic input from user will be treated as **"param"** (useful for Server side rendering) #### Index Routes ``` pages/index.js → / pages/blog/index.js → /blog ``` file name called "index" will automatically routed as root. #### Nested routes ``` pages/foo/bar.js → /foo/bar pages/foo/bar/foobar.js → /foo/bar/foobar ``` #### Dynamic route ``` pages/blog/[foo].js → /blog/:foo (/blog/hello-world) ``` file name with [] will be treated as dynamic routing page. By default, users are expected to enter anything and redirected to this component. ``` pages/post/[...bar].js → /post/* (/post/foo/bar/foobar/i_can_keep_going) ``` Similar with JavaScript/TypeScript, by adding [...], it will catch all the routes afterward. #### getStaticPaths If a page has Dynamic Routes and uses "getStaticProps", it needs to define a list of paths to be statically generated. ```typescript= export async function getStaticPaths() { return { //set the expected params for user paths: [{ params: { id: '1' } }, { params: { id: '2' } }], fallback: false, // can also be true or 'blocking' } } ``` Note: >getStaticPaths must be used with getStaticProps You cannot use getStaticPaths with getServerSideProps ## Api Calling (Axios) ```typescript= //basic get request export const getUser = async (signal:AbortSignal) => { try { const response = await axios.get('/user?ID=12345' , signal); if(response)return response?.data } catch (error) { console.error(error); } } //with useEffect const [user ,setUser] = useState() useEffect(() => { let mount = true const controller = new AbortController(); const fetchUser =async (signal:AbortSignal) =>{ const data = await getUser(signal); if(data)setUser(data) } if(mount)fetchUser(controller.signal) return () => { mount = false controller.abort(); }; }, []); ``` #### Explanation pass in controller.signal to axios has similar effect as clean up effect in useffect hook.When user switch to other pages , we abort the controller and cancel the api request ## React Query ```typescript= import { QueryClient, QueryClientProvider, useQuery } from 'react-query' const queryClient = new QueryClient() export default function App() { return ( <QueryClientProvider client={queryClient}> <Example /> </QueryClientProvider> ) } function Example() { const { isLoading, error, data } = useQuery('repoData', () => fetch('https://api.github.com/repos/tannerlinsley/react-query').then(res => res.json() ) ) if (isLoading) return 'Loading...' if (error) return 'An error has occurred: ' + error.message return ( <div> <h1>{data.name}</h1> <p>{data.description}</p> <strong>👀 {data.subscribers_count}</strong>{' '} <strong>✨ {data.stargazers_count}</strong>{' '} <strong>🍴 {data.forks_count}</strong> </div> ) } ``` ## React Hook Form ```typescript= import { useForm, SubmitHandler } from "react-hook-form"; type Inputs = { example: string, exampleRequired: string, }; export default function App() { const { register, handleSubmit, watch, formState: { errors } } = useForm<Inputs>(); const onSubmit: SubmitHandler<Inputs> = data => console.log(data); console.log(watch("example")) // watch input value by passing the name of it return ( /* "handleSubmit" will validate your inputs before invoking "onSubmit" */ <form onSubmit={handleSubmit(onSubmit)}> {/* register your input into the hook by invoking the "register" function */} <input defaultValue="test" {...register("example")} /> {/* include validation with required or other standard HTML validation rules */} <input {...register("exampleRequired", { required: true })} /> {/* errors will return when field validation fails */} {errors.exampleRequired && <span>This field is required</span>} <input type="submit" /> </form> ); } ``` ## Best Practices and Refs >Team Guidelines 1. fork the repo 2. create a new branch for each task 3. commit frequently by following conventional commits 4. create pull request on github after finishing the task 5. make sure unnecessary changes to other files is removed , you should see your pull request mostly involve additions #### Conventional Commits https://www.conventionalcommits.org/en/v1.0.0-beta.2/ #### React Best Practises https://github.com/mithi/react-philosophies #### Typescript Best Practises https://itnext.io/mastering-typescript-21-best-practices-for-improved-code-quality-2f7615e1fdc3 #### Typescript Design Goals https://github.com/microsoft/TypeScript/wiki/TypeScript-Design-Goals/53ffa9b1802cd8e18dfe4b2cd4e9ef5d4182df10 #### Basic Design Pattern With Web (MVC) https://developer.mozilla.org/en-US/docs/Glossary/MVC #### Design Patterns https://refactoring.guru/design-patterns/factory-method