Skip to content
youhoc
  • Pages
    • Home
    • Modern App Guidelines
    • Linux
      • Day 1: Linux Distributions & Navigation
      • Day 2: User Management
      • Day 3: File Permission & Ownership
      • Day 4: Package Management
      • Day 5: Services Management
    • Javascript
      • JS The Weird Part
        • Execution Context
        • Types & Operators
        • Objects & Functions
        • Error Handling & Strict Mode
        • Typescript, ES6, Tra
      • Modern JS
        • JS in the Browser
        • Data Storage JSON
        • Modern JS
        • Advanced Objects & Methods
        • Webpack & Babel
        • Async
      • jQuery
        • In-depth Analysis of jQuery
      • React-ready JS
        • Arrow Function
        • Template Literals
        • Logical AND, OR, Ternary, Nullish Operators
        • Destructuring & Rest Operator
        • Array Method
        • Immutability and Spread Operator
        • Promises, Async/Await, Callback
    • PHP
      • gruntJS
      • composer
      • MySQL
    • Docker
      • Container Basics
      • Container Networking
      • Container Image
      • Container Volume & Persistent Data
      • Dockerfile
      • Docker Compose
      • Docker Registry
    • Node.js
      • 1. Installing & Exploring
      • 2. Core Modules
      • 3. Get User Input
      • File System & Input Arguments
      • 5. Express Web Server
      • 6. Deploy to Heroku & Github
      • Authentication
      • 7. Databases
      • 8. Rest API
    • ReactJS
      • React from Andrew
        • Summary from Next
        • 1. Basics
        • 2. React Components
        • 3. Webpack
        • 4. Styling with SCSS
        • 5. React Router
        • 6. React Hook
      • Modern React From The Beginning
        • Intro to JSX
        • Vite Build Tools
        • Basic Component Creation
        • Component State
        • Props & Component Composition
        • useState with Inputs & Form Submission
        • icon picker
          useEffect, useRef & Local Storage
        • Async / Await and Http Request in React
        • React Router: Declarative Mode
        • ContextAPI
        • React Router: Framework Mode
          • File-routing & HTML Layouts
          • Server-side Data Query
          • Links & Navigation
          • Loaders
    • Typescript
      • Type User vs UserProp
    • Payload CMS

useEffect, useRef & Local Storage

Tóm tắt

useState

Dùng khi cần render lại UI khi giá trị thay đổi.
Mỗi lần setState → component re-render.
Không nên dùng state để giữ những thứ không ảnh hưởng đến UI (ví dụ: interval ID, biến tạm).

useEffect

Dùng để xử lý side effects khi component mount, update, unmount.
Đặc biệt khi cần làm thêm việc gì đó bên ngoài React (nghĩa là những trigger thay đổi không đến từ bên trong Components mà đến từ những yếu tố bên ngoài).
Nếu truyền [] → chỉ chạy khi mount và cleanup khi unmount.
Nếu truyền [dependency] → chạy lại khi dependency thay đổi.
Nếu không truyền dependency → chạy sau mỗi lần render (cẩn thận dễ gây loop).
Rất hợp để: gọi API, sync localStorage, event listener, timer, document title, WebSocket…

useRef

Dùng để lưu biến cố định trong suốt vòng đời component, nhưng không trigger render khi thay đổi.
Dùng để giữ DOM element:
Dùng để giữ giá trị tạm / flag / previous value.
Dùng để giữ ID của timer / socket connection.

localStorage

Lưu dữ liệu persistent giữa các phiên làm việc.
Chỉ lưu được string → phải JSON.stringify và JSON.parse.
Không thích hợp để lưu dữ liệu quá lớn hoặc thay đổi liên tục (vì tốc độ chậm hơn RAM).
Nên kết hợp với useEffect để sync dữ liệu từ state → storage.

Component Lifecycle

3 trạng thái quan trọng của DOM:
componentDidMount
componentDidUpdate
componentWillUnmount
Traditionally, React components were written with classes and there were certain lifecylce methods that we could use to do something when a component was created, updated, or removed.
Back then, we could still use functional components, but we couldn't tap into these lifecycle methods. They were called dumb components because they were just used to render UI.
Starting in React 16.8, we can now use functional components to tap into these lifecycle methods. We do this with hooks.

Ví dụ về 3 trạng thái của Component Lifecycycle

Dễ hiểu nhất khi đọc Class Component, nhưng dễ viết thì lại là Functional Component.

useEffect

Tại sao phải dùng useEffect để setlocalStorage.setItem mà không setItem ngay bên trong handleSave chẳng hạn?

Nếu bạn set localStorage ngay trong handleSave :

Thì vấn đề là:
Bạn chỉ lưu notes xuống localStorage khi ấn Save.
Nếu state notes thay đổi ở chỗ khác (ví dụ: xóa note, sửa note) mà bạn quên gọi handleSave, thì localStorage sẽ không đồng bộ với state thực tế.
Rất dễ bug kiểu “UI đang khác, reload lại thì dữ liệu lại khác”.

Nếu bạn dùng useEffect

Mỗi lần notes thay đổi → React tự động chạy useEffect.
Bạn không cần nhớ chèn localStorage.setItem vào từng chỗ (add, delete, edit…).
localStorage luôn đồng bộ 100% với state hiện tại.
Đây gọi là “derive side effects from state”. Nghĩa là: cứ coi state là trung tâm, còn việc “phụ” (lưu storage, gọi API, đổi document.title…) thì để useEffect lo.
Vậy nên React mới khuyên: những việc side effect như localStorage, fetch API, setTimeout… nên bỏ vào useEffect thay vì cài chỗ này chỗ kia.

Nhớ kỹ 3 dạng dependency phổ biến

[] chạy duy nhất 1 lần khi mount (thường cho fetch API, addEventListener, init lib).
[state/props] chạy lại khi có thay đổi (document.title, localStorage sync).
Không có dependency: chạy mỗi lần render (hiếm khi cần, dễ tốn hiệu năng).

Việc “bên ngoài React” (side effect) là gì?

Là những việc không chỉ ảnh hưởng đến UI trong React, mà còn ảnh hưởng đến môi trường bên ngoài.
Ví dụ:
Gọi API → dữ liệu từ server (React không tự làm được).
Lưu vào localStorage → ghi dữ liệu xuống browser.
Đăng ký sự kiện window.addEventListener("scroll", ...).
Đặt setInterval hoặc setTimeout.
Thay đổi document.title trên tab trình duyệt.
Kết nối WebSocket / Firebase.
Tất cả những cái này vượt ra ngoài phạm vi React render → React gọi chung là side effects.

Tại sao phải tách ra useEffect?

Nếu bạn nhét side effect vào thẳng trong function component, thì:
Nó sẽ chạy mỗi lần render (dễ lỗi, gây lặp vô hạn).
Không có chỗ để cleanup (gỡ bỏ listener, clear interval…).
useEffect sinh ra để:
Chạy code sau khi render xong.
Cho phép bạn quản lý lifecycle (setup + cleanup).
Chỉ chạy khi bạn muốn (nhờ dependency array).

Ví dụ sai và đúng

Gọi API trực tiếp trong render functional component

Ứng dụng phổ biến của useEffect

Đồng bộ state với localStorage

khi khởi động app, tự động lấy dữ liệu từ localStorage trước
tự động lưu state của app vào localStorage mỗi khi state thay đổi

Fetch API / gọi server

Khi component mount, gọi API để lấy dữ liệu ban đầu:

Đăng ký / gỡ bỏ event listener

Ví dụ: lắng nghe sự kiện resize window, scroll, keydown…

Cập nhật document title, meta, URL…

Kết nối Websocket / Real-time

Set interval / timeout

Đồng bộ dữ liệu với các lib ngoài React

Ví dụ: tích hợp bản đồ (Leaflet, Google Maps), chart (Chart.js), hoặc thư viện UI (Bootstrap tooltip, jQuery plugin…).

useEffect & Local Storage

I want to perform a side effect when the notes are updated: save the notes to local storage.
I can do this by using the `useEffect` hook. If we were using class components, we would use the `componentDidUpdate` lifecycle method.
Remember, the useEffect runs when the component mounts regardless of the dependencies.
In this case, the useEffect will run when the component mounts and when the notes change.
You can open your devtools and click on the Application tab. You will see the notes in local storage when you submit.
However when you referesh the page, the notes will disappear. Even from local storage.
This may be confusing. What's happening is when we refresh the page, the notes are being reset to the initial state. And remember, the useEffect runs when the notes are updated. So we are essentially overwriting the notes in local storage with the initial state.
However, there is an issue with this because setNotes is asynchronous.
This means that the notes will not be loaded in time for the useEffect to save the notes to local storage. This will cause the notes to be overwritten with the initial state.
To fix this, we can make the initial state from the `useState` hook a function that returns the notes from local storage.
We are using a function to set the initial state. This function will run once when the component mounts.
This will allow us to load the notes from local storage before the `useEffect` hook runs. Now the notes will be loaded from local storage when the component mounts and saved to local storage when the notes change.

useRef

It allows us to reference any DOM element directly, and we can then use the reference to access or manipulate that element — for example, setting focus to an input, reading its value, or even measuring its dimensions without triggering a re-render.
This is similar to how you might use document.querySelector in vanilla JavaScript, but it’s the React way to reference DOM elements in functional components.
useRef is used for uncontrolled components
It can also be used to persist values across renders without causing a re-render.
It creates a mutable reference that persists across renders

useRef và useState

Giả dụ chúng ta cần xây dựng một Timer App có nút Stop và Reset.
Thì dưới đây là 2 ví dụ hoạt động giống nhau, chỉ khác nhau ở những chỗ rất nhỏ, mà khuyến nghị là nên dùng useRef để giảm bớt rủi ro.
Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.