🖍️
Developer Note
  • Welcome
  • Git
    • Eslint & Prettier & Stylelint & Husky
  • Programming Language
    • JavaScript
      • Script Async vs Defer
      • Module
      • Const VS Let VS Var
      • Promise
      • Event Loop
      • Execution Context
      • Hoisting
      • Closure
      • Event Buddling and Capturing
      • Garbage Collection
      • This
      • Routing
      • Debounce and Throttle
      • Web Component
      • Iterator
      • Syntax
      • String
      • Array
      • Object
      • Proxy & Reflect
      • ProtoType
      • Class
      • Immutability
      • Typeof & Instanceof
      • Npm (Node package manager)
    • TypeScript
      • Utility Type
      • Type vs Interface
      • Any vs Unknown vs Never
      • Void and undefined
      • Strict Mode
      • Namespace
      • Enum
      • Module
      • Generic
    • Python
      • Local Development
      • Uv
      • Asyncio & Event loop
      • Context Manager
      • Iterator & Generator
      • Fast API
      • Pydantic & Data Class
    • Java
      • Compilation and Execution
      • Data Type
      • Enumeration
      • Data Structure
      • Try Catch
      • InputStream and OutputStream
      • Concurrent
      • Unicode Block
      • Build Tools
      • Servlet
      • Java 8
  • Coding Pattern
    • MVC vs MVVM
    • OOP vs Functional
    • Error Handling
    • MVC vs Flux
    • Imperative vs Declarative
    • Design Pattern
  • Web Communication
    • REST API
      • Web Hook
      • CORS issue
    • HTTPS
    • GraphQL
      • REST API vs GraphQL
      • Implementation (NodeJS + React)
    • Server-Sent Event
    • Web Socket
    • IP
    • Domain Name System (DNS)
  • Frontend
    • Progressive Web App (PWA)
    • Single Page & Multiple Page Application
    • Search Engine Optimiaztion (SEO)
    • Web bundling & Micro-frontend
      • Webpack
        • Using Webpack to build React Application
        • Using Webpack to build react library
      • Vite
      • Using rollup to build react library
      • Implementing micro frontend
    • Web Security
      • CSRF & Nonce
      • XSS
      • Click hijacking
    • Cypress
    • CSS
      • Core
        • Box Model
        • Inline vs Block
        • Flexbox & Grid
        • Pseudo Class
        • Position
      • Tailwind CSS
        • Shadcn
      • CSS In JS
        • Material UI
    • React
      • Core
        • Component Pattern
        • React Lazy & Suspense
        • React Portal
        • Error Boundary
        • Rendering Methods
        • Environment Variable
        • Conditional CSS
        • Memo
        • Forward Reference
        • High Order Component (HOC) & Custom Hook
        • TypeScript
      • State Management
        • Redux
        • Recoil
        • Zustand
      • Routing
        • React Router Dom
      • Data Fetching
        • Axios & Hook
        • React Query
        • Orval
      • Table
        • React Table
      • Form & Validation
        • React Hook Form
        • Zod
      • NextJS
        • Page Router
        • App Router
      • React Native
    • Angular
    • Svelte
      • Svelte Kit
  • Backend
    • Cache
      • Browser Cache
      • Web Browser Storage
      • Proxy
      • Redis
    • Rate limit
    • Monitoring
      • Logging
      • Distributed Tracing
    • Load Test
    • Encryption
    • Authentication
      • Password Protection
      • Cookie & Session
      • JSON Web Token
      • SSO
        • OAuth 2.0
        • OpenID Connect (OIDC)
        • SAML
    • Payment
      • Pre-built
      • Custom
    • File Handling
      • Upload & Download (Front-end)
      • Stream & Buffer
    • Microservice
      • API Gateway
      • Service Discovery
      • Load Balancer
      • Circuit Breaker
      • Message Broker
      • BulkHead & Zipkin
    • Elastic Search
    • Database
      • SQL
        • Group By vs Distinct
        • Index
        • N + 1 problem
        • Normalization
        • Foreign Key
        • Relationship
        • Union & Join
        • User Defined Type
      • NOSQL (MongoDB)
      • Transaction
      • Sharding
      • Lock (Concurrency Control)
    • NodeJS
      • NodeJS vs Java Spring
      • ExpressJS
      • NestJS
        • Swagger
        • Class Validator & Validation Pipe
        • Passport (Authentication)
      • Path Module
      • Database Connection
        • Integrating with MYSQL
        • Sequalize
        • Integrating with MongoDB
        • Prisma
        • MikroORM
        • Mongoose
      • Streaming
      • Worker Thread
      • Passport JS
      • JSON Web Token
      • Socket IO
      • Bull MQ
      • Pino (Logging)
      • Yeoman
    • Spring
      • Spring MVC
      • Spring REST
      • Spring Actuator
      • Aspect Oriented Programming (AOP)
      • Controller Advice
      • Filter
      • Interceptor
      • Concurrent
      • Spring Security
      • Spring Boot
      • Spring Cloud
        • Resilience 4j
      • Quartz vs Spring Batch
      • JPA and Hibernate
      • HATEOS
      • Swagger
      • Unit Test (Java Spring)
      • Unit Test (Spring boot)
  • DevOp
    • Docker
    • Kubernetes
      • Helm
    • Nginx
    • File System
    • Cloud
      • AWS
        • EC2 (Virtual Machine)
        • Network
        • IAM
          • Role-Service Binding
        • Database
        • Route 53
        • S3
        • Message Queue
        • Application Service
        • Serverless Framework
        • Data Analysis
        • Machine Learning
        • Monitoring
        • Security
      • Azure
        • Identity
        • Compute Resource
        • Networking
        • Storage
        • Monitoring
      • Google Cloud
        • IAM
          • Workload Identity Federation
        • Compute Engine
        • VPC Network
        • Storage
        • Kubernetes Engine
        • App Engine
        • Cloud function
        • Cloud Run
        • Infra as Code
        • Pub/Sub
    • Deployment Strategy
    • Jenkins
    • Examples
      • Deploy NextJS on GCP
      • Deploy Spring on Azure
      • Deploy React on Azure
  • Domain Knowledge
    • Web 3
      • Blockchain
      • Cryptocurrency
    • AI
      • Prompt
      • Chain & Agent
      • LangChain
      • Chunking
      • Search
      • Side Products
Powered by GitBook
On this page
  • Life Cycle
  • Re-render
  • Hook
  • useState
  • useEffect
  • UseContext
  • useReducer
  • useCallback
  • useMemo
  • useRef
  • useImperativeHandle
  • useLayoutEffect
  • useTransition
  • useDeferredValue
  • useOptimistic (React 19)
  • useActionState (React 19)
  • Custom Hook

Was this helpful?

  1. Frontend

React

PreviousMaterial UINextCore

Last updated 4 days ago

Was this helpful?

Life Cycle

Re-render

  • There is element in the index.html which id is root and render the react dom element

  • The react dom object itself is a virtual dom which is lightweight, which looks like:

import React from 'react'
import { createRoot } from 'react-dom/client'

const spanElement = React.createElement('span', {
  children: '123'
});
console.log(spanElementElement);
console.log(<span>123</span>);
/* 
{
  "type":"span",
  "key":null,
  "ref":null,
  "props":{"children":"123"},
  "_owner":null,
  "_store":{}
} 
*/
createRoot(document.getElementById('root')!).render(
  spanElement
)
  • For every state change, the page will be re-rendered again, the state , variable , function, useEffect in the render scope will be independent to previous one

  • For each rendering, the functional component will be returned and then triggered useEffect

  • The virtual dom will be saved in memory and will be compared with the previous virtual dom with its internal differing algorithm, and just update the changed part on the real dom which is into dom which id is root so as to increase the efficiency

Hook

useState

  • When you call setState, React schedules the state update and marks the component for a re-render. This scheduling is asynchronous

  • React batched state updates in event handlers to reduce the number of re-rendering

// number is a state, setNumber is a setter method
const [number, setNumber] = useState(1);

// put the value into setter to set the value directly
setNumber(2);
// put the update function into setter 
// to set the value which is based on previous value
setNumber(prevNum => prev + 1);
// Not recommended
setNumber(number + 1);
function A () {
    const [count, setCount] = useState(4);
    setCount(count + 1);
    setCount(count + 1);
    console.log('A: ', count) // ?
}
function B () {
    const [count, setCount] = useState(4);
    setCount(prev => prev + 1);
    setCount(prev => prev + 1);
    console.log('B: ', count) // ?
}
// Answer
// A: 5
// B 6
function init () {
    console.log('run function');
    return 4;
}
// Run Everytime when changing the state
const [count, setCount] = useState(4);
const [count, setCount] = useState(init());
// Run only the very first time when your component render
const [count, setCount] = useState(() => init());

useEffect

  • It is used to listen the dependencies change, e.g: state, and execute the side effect, which is equal to lifecycle event of class component

  • When the component is mounted, the side effect will be triggered once first.

  • When component is unmounted, the return function will be triggered and based on the previous useEffect. After the new user interface finish re-rendering, , and finally trigger the new useEffect function

"use client"

import { Button,} from "@mui/material";
import { useEffect,useState } from "react";
export default function Home() {
  const [num, setNum] = useState<number>(0);
  useEffect(()=>{
    console.log("update" , num);
    return ()=> {
      console.log("return function", num);
    }
  },[num])

  // update 0
  // return function 0
  // update 1
  return (
    <div>
       <Button variant="contained" onClick={()=>{
        setNum((prev)=>prev+1)
       }} >Hello world</Button>
    </div>
  );
}
  • The function of useEffect will always be triggered in every re-rendering as there is no dependency array

useEffect(async () => {
    const { data } = await axios.get(
      "https://sampleapis.com/futurama/api/info",
      {}
    );
    console.log("data", data);
  });
  • The function of useEffect will only be triggered once as the dependency array is empty, there will not have any dependency change to trigger useEffect

useEffect(async () => {
    const { data } = await axios.get(
      "https://sampleapis.com/futurama/api/info",
      {}
    );
    console.log("data", data);
  },[]);
  • The function of useEffect will be triggered due to the change of the state

 useEffect(async () => {
    const { data } = await axios.get(
      "https://sampleapis.com/futurama/api/info",
      {}
    );
    console.log("data", data);
  }, [state]);

Case Study 1

  • As the state and variable in render scope is put in the useEffect, it is not recommended to put an empty array as it may cause potential bug

const App = () =>{
    const [count , setCount] = useState(0);
    const test = 2; 
    // Wrong
    useEffect(()=>{
        setCount(count+1);
        test = 3;
    },[])
    // Correct
    useEffect(()=>{
        setCount(count+1);
    }, [count, test])
}

Case Study 2

  • As the function in render scope is put in useEffect, it is recommended to put the function in the dependency array

  • As we do not want the useEffect always be triggered as the function is always be changed in every re-rendering, so we use useCallback to save the function except the dependency is changed

const App = () =>{
    const [count , setCount] = useState(0);
    
    // Wrong
    useEffect(()=>{
        addCount();
    }, [])
    
    const addCount = () => {
        setCount(count+1);
    }
    
    // Correct
    useEffect(() => {
        addCount();
    }, [addCount])
    
    const addCount = useCallback(() => {
        setCount(count+1);
    }, [count])
}

Case Study 3

  • As async function have the delay, we cannot make sure of order of the setState. In order to prevent from set the wrong data which is based on the the value or state in previous rendering, it is recommended to make return function to stop setting wrong value after the state is updated

function Article({ id }) {
  const [article, setArticle] = useState(null);

  useEffect(() => {
    let didCancel = false;

    async function fetchData() {
      const article = await API.fetchArticle(id);
      if (!didCancel) {
        setArticle(article);
      }
    }

    fetchData();

    return () => {
      didCancel = true;
    };
  }, [id]);

  // ...
}

UseContext

  • Share all the data from component with upper layer to components with lower layer without passing props

// Mother.js
import React, { createContext } from "react";
import Son from "./Son";
type ParentContext = {
  data:string
  setData: (data:string) => void
}
export const ParentContext = createContext<ParentContext>({data: ""});
const Mother = () => {
  const [data, setData] = useState("Mother");
  return (
    <ParentContext.Provider value={{data,setData}}>
      <Son />
    </ParentContext.Provider>
  );
};

export default Mother;
// Son.js
import React, { useContext } from "react";
import { ParentContext } from "./context";
const Son = () => {
  const {data,setData} = useContext(ParentContext);
  return <span>{data}</span>;
};
export default Son;

useReducer

  • It is an advanced version of useState, since the setting function of the useState is only allow one situation and one return value. But useReducer can return different base on the the type, it is more suitable to handle more complicated logic

// 一個 Reducer 用來描述根據指令執行對應的動作,會回傳一個新的 state 物件,是個純函式
const todoReducer = (state, action) => {
    // 判斷指令
    switch (action.type) {
        case 'ADD_TODO':
            let newKey = 1
            if (state.list.length !== 0){
                newKey = state.list[state.list.length - 1].listKey + 1
            }
            return {
                ...state,
                list: [...state.list, { listKey: newKey, name: action.payload.name }]
            }
        default:
            return state
    }
}

// 初始資料,為 state 的初始值,通常會在這裡把結構描述完整
const initState = {
    list: [{ listKey: 1, name: 'first' }]
}

const Todo = () => {
    const [listName, setListName] = useState('')
    
    /*
     使用 useReducer 將 Reducer 和 初始 State 個別傳入,
     會回傳一個陣列,在此使用解構賦值將陣列的位置 0 及 1 分別放到 state 和 dispatch 中,
     state 會隨著 Reducer 會傳的 state 做改變,
     dispatch 是用來和 Reducer 溝通的 Function 。
    */
    const [state, dispatch] = useReducer(todoReducer, initState)
    const addTodo = () => {
        //使用 dispatch 呼叫 Reducer 處理 state ,這個參數會被傳進 Reducer 的 action 中
        dispatch({ type: 'ADD_TODO', payload: { name: listName } })
        setListName('')
    }

    return (
        <div>
            <input value={listName}
                onChange={e => setListName(e.target.value)} />
            <input type='button' value='新增' onClick={addTodo} />
            {state.list.map((list) => {
                return (
                    <p key={list.key}>
                        <input type='button' value='移除'
                            onClick={() => { removeTodo(list.listKey) }} />
                        {list.name}
                    </p>
                )
            })}
        </div>
    )
}

useContext/useReducer vs Redux

  • useContext/useReducer : it is easier to setup and more suitable to put variables with low-frequency update on it , since every time the variable passed by useContext, the area inside the provider will be re-rendered

  • Redux: it is more complicated to setup, and middleware is allowed to use to handle async function. each component will subscribe (add listener) to their connected state. If the correspondent state is changed, the listener will be triggered, so that only that component will be re-rendered but not whole of the area inside the provider

useCallback

  • When the component is re-rendered, the functions will also be re-rendered. The purpose of useCallback is return the memorized callback function until the one of the dependency is changed.

 const [delta, setDelta] = useState(1);
 const [c, setC] = useState(0);

 // the function will keep the same, which means delta always be 1,
 // and listen to the dependency c , if c is changed , 
 // the function will be re-rendered and updated
 const incrementDeltaWithCallBack = useCallback(() => {
    setDelta(delta + 1);
    console.log("increaseDelta");
  }, [c]);
  
 const incrementWithCallBack = useCallback(() => {
    setC(c + 1);
    console.log("increment");
  }, [delta]);

useMemo

  • When the component is re - rendered, the variable will also be re-rendered. The purpose of useMemo is return the memorized value until the one of the dependency is changed.

  const [delta, setDelta] = useState(1);
  
  // even setDelta is triggered, but value of testMemo will 
  // be the same, always be 1, will not be re-rendered
  const testMemo = useMemo(() => delta, []);
  

useMemo vs useCallback

  • The effect of useMemo can equal to useCallback in this case

 const incrementDeltaWithCallBack = useCallback(() => {
    setDelta((delta) => delta + 1);
    console.log("increaseDelta");
  }, [delta]);
  
 const incrementDeltaWithMemo = useMemo(() => () => {
    setDelta((delta) => delta + 1);
    console.log("increaseDelta");
  }, [delta]);

The drawback of abusing useMemo/ useCallback

  • The time of the first time re-rendering will be much larger as it memorizes the value/function

  • The ram consumed in the browser will be much larger

The condition of using useMemo/ useCallback

  • The variable require long time to be rendered , for example : have a larger loop and return the result

  • The function is called in useEffect

useRef

  • The current property will be initialized by passing the argument , and if the current is changed, it will be saved, but the component will not be re-rendered, it can be used to store the value which is not related to the ui

  const testRef = useRef(0);
  
  const test = () => {
    console.log(testRef.current); // 0
    testRef.current++;
    return (<div>{testRef.current}</div>)
    // next time testRef.current will be 1 ...
    // but the ui still show 0 
  }
  • The reference object can be acted as the ref of the element, so that the current property will be corresponded to the html element. We can make use good use of the reference to manipulate the element, such as focus

const App = () => {
    const testRef = useRef();
    const test = () => {
        console.log(testRef.current); // <input..../>
        testRef.current.focus();
     }
    return (
        <div>
            <input ref={testRef} type="text"/>
        </div>
    )
}

useImperativeHandle

  • To lets you customize the handle exposed as a ref

  • Mainly for exposing the child state, method to the parent or your own method

App.js
import MyInput from './MyInput.js';

export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    // to apply the method passed by child component
    ref.current.focus();
  }

  return (
    <form>
      <MyInput placeholder="Enter your name" ref={ref} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}
MyInput.js
import { forwardRef, useRef, useImperativeHandle } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
  const inputRef = useRef(null);

  useImperativeHandle(ref, () => {
    return {
      focus() {
        inputRef.current.focus();
      },
      scrollIntoView() {
        inputRef.current.scrollIntoView();
      },
    };
  }, []);

  return <input {...props} ref={inputRef} />;
});

export default MyInput;

useLayoutEffect

function Tooltip() {
  const ref = useRef(null);
  const [tooltipHeight, setTooltipHeight] = useState(0); // You don't know real height yet

  useLayoutEffect(() => {
    const { height } = ref.current.getBoundingClientRect();
    setTooltipHeight(height); // Re-render now that you know the real height
  }, []);

  // The component only render once when using layout effect
  // useLayoutEffect: render with height
  // useEffect: render with 0 -> render with height
}

useTransition

  • The function you pass to startTransition must be synchronous. React immediately executes this function, marking all state updates that happen while it executes as Transitions.

  • The hook can be used prioritize the state update . A state update marked as a Transition will be interrupted by other state updates and marked as lower priority

  • If there are multiple ongoing Transitions, React currently batches them together.

  • The state update inside the startTransition will be non-blocking, which means that the state update will not be blocked by heavy computation and be freeze, it will interrupt the slow rendering immediately

import { useTransition } from 'react';

 const [isPending, startTransition] = useTransition();

// Urgent: Show what was typed
setInputValue(input);

// Mark any non-urgent state updates inside as transitions
startTransition(() => {
  // Transition: Show the results
  setSearchQuery(input);
});

useDeferredValue

  • The deferred value is considered as a low priority, its deferred “background” rendering is interruptible even if the deferred value is updated.

  • For example, if you type into the input again, React will abandon it and restart with the new value. Also, network request is pending, the rendering is also be suspended

function App() {
  const [text, setText] = useState('');
  const deferredText = useDeferredValue(text);
  return (
    <>
      <input value={text} onChange={e => setText(e.target.value)} />
      <SlowList text={deferredText} />
    </>
  );
}
  • useDeferredValue lets you prioritize updating the input (which must be fast) over updating the result list (which is allowed to be slower)

useOptimistic (React 19)

  • It provides a function that takes the current state and the input to the action, and returns the optimistic state to be used while the action is pending.

  • The state is called the “optimistic” state because it is usually used to immediately present the user with the result of performing an action, even though the action actually takes time to complete.

import { useOptimistic, useState, useRef } from "react";
import { deliverMessage } from "./actions.js";

function Thread({ messages, sendMessage }) {
  const formRef = useRef();
  async function formAction(formData) {
    addOptimisticMessage(formData.get("message"));
    formRef.current.reset();
    await sendMessage(formData);
  }
  const [optimisticMessages, addOptimisticMessage] = useOptimistic(
    messages,
    (state, newMessage) => [
      ...state,
      {
        text: newMessage,
        sending: true
      }
    ]
  );

  return (
    <>
      {optimisticMessages.map((message, index) => (
        <div key={index}>
          {message.text}
          {!!message.sending && <small> (Sending...)</small>}
        </div>
      ))}
      <form action={formAction} ref={formRef}>
        <input type="text" name="message" placeholder="Hello!" />
        <button type="submit">Send</button>
      </form>
    </>
  );
}

export default function App() {
  const [messages, setMessages] = useState([
    { text: "Hello there!", sending: false, key: 1 }
  ]);
  async function sendMessage(formData) {
    const sentMessage = await deliverMessage(formData.get("message"));
    setMessages((messages) => [...messages, { text: sentMessage }]);
  }
  return <Thread messages={messages} sendMessage={sendMessage} />;
}

useActionState (React 19)

  • It pass useActionState an existing form action function as well as an initial state, and it returns a new action that you use in your form, along with the latest form state and pending state

function ChangeName({ name, setName }) {
  const [error, submitAction, isPending] = useActionState(
    async (previousState, formData) => {
      const error = await updateName(formData.get("name"));
      if (error) {
        return error;
      }
      redirect("/path");
      return null;
    },
    null,
  );

  return (
    <form action={submitAction}>
      <input type="text" name="name" />
      <button type="submit" disabled={isPending}>Update</button>
      {error && <p>{error}</p>}
    </form>
  );
}

Custom Hook

  • Can do a custom hook and apply to different component instead of making high order component

useLayoutEffect is a version of that fires before the browser repaints the screen, to enhance the user experience and the shift of component

The code inside useLayoutEffect and all state updates scheduled from it block the browser from repainting the screen. When used excessively, this makes your app slow. When possible, prefer

useEffect
useEffect.
LogoWhat is Virtual Dom? And Why is it faster?DEV Community
LogoReconciliation – React
The real dom