Tại sao React component của tôi re-render 2 lần?

Châm ngôn của mình là học để kiếm tiền.

Vì thế mình build các khóa học của mình để giúp anh em tiến bộ nhanh hơn x10 lần , để kiếm được nhiều tiền hơn

  • 🏆 React.js Super: Trở thành React.js Developer trong 7 ngày với mức thu nhập 20 triệu/tháng
  • 🏆 Node.js Super: Giúp bạn học cách phân tích, thiết kế, deploy 1 API Backend bằng Node.js
  • 🏆 Next.js Super: Mình sẽ chia sẻ từ A-Z kiến thức về Next.js, thứ giúp mình kiếm hơn 1 tỉ/năm

Bài viết này Được sẽ lý giải một số trường hợp component React của bạn bị re-render 2 lần dù bạn đã kiểm tra kĩ lắm rồi nhưng vẫn không phát hiện lỗi sai.

Trước tiên mình sẽ liệt kê một số lý do làm component React của các bạn bị re-render

🥇Những trường hợp làm React Component Re-render

  • Cập nhật state mới cho component
  • Thay đổi props component
  • Thay đổi url khi sử dụng router
  • Component cha re-render dẫn đến component con re-render

Đó là những trường hợp phổ biến nhất mà ai cũng đều biết. Chúng ta có thể khắc phục bằng cách dùng useMemo, useCallback, hay React.memo.

Bài viết này mình không đề cấp đến những cái trên mà là việc

  • callback trong useEffect chạy 2 lần do dùng React Strict Mode
  • Cập nhật state giống state cũ nhưng component vẫn re-render

🥇Sử dụng React Strict Mode làm component bị mount và unmount

Đây là trường hợp phổ biến và hay gặp nhất, nhiều bạn mới code React không hiểu tại sao lại thêm cái Strict Mode này vào làm gì để cho việc kiểm soát re-render của các bạn bị rối.

Đúng là thoạt nhìn qua thì thấy rối thật, nó làm component của các bạn mount (khởi tạo) rồi lại unmount (hủy)

Dẫn đến code xử lý trong component bị chạy 2 lần (hãy xem đoạn code console.count("Render");) và callback trong useEffect bị gọi 2 lần (xem đoạn code console.count("Số lần Callback trong useEffect chạy");).

Nhìn ví dụ dưới đây

Tất cả lý do trên đều do React Strict Mode gây nên, nếu bạn tắt React Strict Mode thì sẽ không còn gặp hiện tượng đó nữa. Và React Strict Mode chỉ hoạt động ở môi trường dev thôi, khi build ra production thì nó không hoạt động.

🥈Vậy tác dụng của React Strict Mode là gì?

Như cái tên gọi của nó, đây là "Chế độ an toàn cho React", nó sẽ giả sử các trường hợp có thể xảy ra trong thực tế khi ứng dụng bạn chạy

Ví dụ mình gọi API trong useEffect() như thế này thì API sẽ bị gọi 2 lần ngay lần đầu app mình chạy

Books.jsx

jsx
function Books() {
  const [books, setBooks] = useState([])
  useEffect(() => {
    getBooks().then((res) => {
      setBooks(res)
    })
  }, [])
  return <div>Books</div>
}

App.jsx

jsx
import Books from './Books'
export default function App() {
  return (
    <div className='App'>
      <Books />
    </div>
  )
}

Ý React Strict Mode muốn nói ở đây là trong thực tế cái API đó có thể bị gọi liên tiếp 2 lần và có thể gây ảnh hưởng đến ứng dụng của bạn.

Lúc này bạn nghĩ "Làm éo gì có chuyện gọi 2 lần API như việc Strict Mode mô phỏng 🤣"

Vậy nếu bây giờ component App của các bạn như thế này và bạn nhanh tay ấn liên tục button thì component Books của bạn sẽ bị mount / unmount liên tục dẫn đến API gọi liên tục.

jsx
import Books from './Books'
export default function App() {
  const [visible, setVisible] = useState(true)
  return (
    <div className='App'>
      <button onClick={()=> setVisible((prev)=> !prev)}>Ẩn hiện Books</button>
      {visible && <Books />}
    </div>
  )
}

Và trường hợp có thể xảy ra là khi component Books bị unmount và sau đó thì api gọi xong, nó tiến hành setBooks(res) ở một component đã bị hủy 🤣

Đấy! Thực tế điều này vẫn có thể xảy ra.

Vậy khi chúng ta thấy gọi API 2 lần ở useEffect thì chúng ta nên sử dụng clean up function cho useEffect

Books.jsx

jsx
function Books() {
  const [books, setBooks] = useState([])
  useEffect(() => {
    getBooks().then((res) => {
      setBooks(res)
    })
    return () => {
      // Cancel Request tại đây!
      // controller.abort()
    }
  }, [])
  return <div></div>
}

Cancel request bằng AbortController đều có sẵn trên AxiosFetch API

Nếu các bạn lắng nghe sự kiện trong useEffect thì cũng nhớ nên hủy việc lắng nghe trong clean up function nhé

💡 Mẹo:

Nếu anh em dùng React Query thì nó xử lý giúp chúng ta mấy cái việc lúc nào cũng phải nhớ dùng clean up cho các sự kiện gọi api. Rất tiện luôn. Đó là lý do mình đã chuyển sang dùng React Query và khóa học ReactJs Super của mình cũng dạy React Query cho các bạn 💓

🥇Component re-render 2 lần dù setState cùng giá trị giống nhau

Điều kiện để component của bạn re-render khi dùng setState là chúng ta phải setState với giá trị khác với state hiện tại (React sử dụng thuật toán so sánh Object.is())

  • Đối với kiểu dữ liệu nguyên thuỷ thì khác giá trị
  • Đối với object thì khác tham chiếu

Nhưng ta sẽ gặp một trường hợp như dưới đây.

Khi nhấn button lần đầu tiên thì re-render 1 lần (Mary thành Dư Thanh Được)

Nhấn lần thứ 2 thì re-render lần thứ nữa mặc dù 2 giá trị trước và sau không thay đổi (Dư Thanh Được thành Dư Thanh Được)

Nhấn lần thứ 3 thì không còn thấy hiện tượng re-render nữa.

jsx
export default function App() {
  const [name, setName] = useState('Mary')
  return (
    <div>
      <button onClick={()=> setName('Dư Thanh Được')}>Change Name</button>
      <div>{name}</div>
    </div>
  )
}

Các bạn xem demo dưới dây, mình không dùng React Strict Mode trong trường hợp này để các bạn đỡ rối khi nhìn vào console log

Theo như Team React giải thích tại sao component re-render 2 lần dùng setState cùng giá trị là

  • Khi bạn nhấn button ở lần 2, React sẽ không biết liệu bạn có thực sự muốn set state và re-render hay không nên React sẽ re-render.

  • Ở lần nhấn button thứ 3, khi chúng ta lại set với trị cũ thì bây giờ React sẽ không re-render nữa, vì cả 2 state đều giống nhau.

🥇Tóm lại

Các bạn thấy đó, mọi chuyện trên đời đều có lý do của nó cả 😁, hy vọng bài viết này có thể giải đáp được các thắc mắc của các bạn bấy lâu nay.


Kiến thức trong khóa học Next.js này đã giúp mình kiếm hơn 1 tỉ đồng/năm

Phew! Cuối cùng bạn cũng đã đọc xong. Bài viết này có hơi dài một tí vì mình muốn nó đầy đủ nhất có thể 😅

Website bạn đang đọc được viết bằng Next.js TypeScript và tối ưu từng chi tiết nhỏ như SEO, hiệu suất, nội dung để đảm bảo bạn có trải nghiệm tốt nhất.

Với lượt view trung bình là 30k/tháng (dù website rất ít bài viết). Website này đem lại doanh thu 1 năm vừa qua là hơn 1 tỉ đồng

Đó chính là sức mạnh của SEO, sức mạnh của Next.js.

Mình luôn tin rằng kiến thức là chìa khóa giúp chúng ta đi nhanh nhất.

Mình đã dành hơn 6 tháng để phát triển khóa học Next.js Super | Dự án quản lý quán ăn & gọi món bằng QR Code. Trong khóa này các bạn sẽ được học mọi thứ về framework Next.js, các kiến thức từ cơ bản cho đến nâng cao nhất, mục đích của mình là giúp bạn chinh phục mức lương 25 - 30 triêu/tháng

Nếu bạn cảm thấy bài viết này của mình hữu ích, mình nghĩ bạn sẽ thích hợp với phong cách dạy của mình. Không như bài viết này, khóa học là sự kết hợp giữa các bài viết, video, bài tập nhỏ và dự án lớn có thể xin việc được ngay. Học xong mình đảm bảo bạn sẽ lên tay ngay. 💪🏻

Avatar Dư Thanh Được

Dư Thanh Được

Một developer thích nghiên cứu và chia sẻ kiến thức về lập trình, blockchain, marketing. Chuyên code và dạy lập trình website