Dạo gần đây mình hay bắt gặp trên TikTok một câu nói như này "1 mét vuông 100 thằng Frontend", hay có thể nói cụ thể hơn là "1 mét vuông có 100 thằng Frontend xài React Js". Là một software engineer và cũng đang làm frontend (React Js) nghe câu này mình cũng cảm thấy đôi chút chạnh lòng, cũng như một chút gì đó "tự ái", vì cứ như thể làm một Frontend developer là cái gì đó đơn giản, và không đáng được coi trọng vậy... Thế nên mình quyết định sẽ viết một cái gì đó (dựa vào kiến thức và trải nghiệm của cá nhân mình) để mọi người có thể hiểu hơn về công việc của 1 frontend developer không hề dễ dàng một chút nào. Hmm hold on có cái gì đấy cần làm rõ một chút ở đây, đúng là hiện tại số lượng lập trình viên frontend trên thị trường khá nhiều, vì hầu như mọi người khi mới học lập trình thì đều chọn frontend, có thể là do nó dễ để bắt đầu, chỉ cần một chút kiến thức về CSS, HTML là đã có thể thấy ngay thành quả rồi. Dễ bắt đầu là thế nhưng để nắm vững nó để trở thành một Frontend developer đủ tốt thì nhiêu đấy thôi là chưa đủ. Điển hình như việc bạn sử dụng React Js nhưng có thật sự đã hiểu về nó đủ sâu chưa ?. Okie luyên thuyên thế đủ rồi vào vấn đề chính thôi.

1. React Js là cái gì ?.

Đã có quá nhiều bài viết nói về định nghĩa của React Js rồi, ngay cả trên trang chủ của React Js cũng có nói đến vấn đề này, nên mình xin tóm gọn lại như sau: React Js là một thư viện viết bằng Javascript để giúp cho việc tạo ra giao diện người dùng trên web (hoặc đôi khi là trên mobile) một cách dễ dàng hơn.

2. Nguyên lý hoạt động của React Js

Có thể nhiều người làm việc đủ lâu với React thì cũng không lạ gì về cái này nhưng mình vẫn muốn làm rõ hơn để cho các bạn mới học có cái nhìn chi tiết hơn về nó. Mình xin mượn tạm bức ảnh dưới đây để mô tả về cách thức mà React (một React's component) hoạt động. Có nhiều tài liệu sẽ ghi chi tiết hơn về các methods trong một life cycle của một React's component (cái mà một số bạn khi mới tiếp xúc với React phiên bản hook gần đây có thể sẽ không nắm). Nhưng đừng lo các bạn chỉ cần biết rằng React hoạt động sẽ bao gồm 3 giai đoạn. Mounting, đây là công đoạn chuẩn bị những thứ cơ bản của một component (state và props) trong method constructor, tiếp đến React sẽ render component (nôm na có thể hiểu là React sẽ gắng component của mình lên cây DOM thật của browser trong lần render đầu tiên). Updating, từ sau lần render đầu tiên, mỗi khi state hay props thay đổi component của chúng ta sẽ được render lại để cập nhật lại giao diện với những giá trị mới của state / props quá trình gọi là re-render. các bạn có thể hiểu nó giống như một vòng lập vô tận theo thứ tự: state / props thay đổi => trigger re-render => vẽ lại giao diện mới => rồi lại chờ đợi state / props thay đổi để lặp lại quá trình trên. Để tiết kiệm thời gian mình sẽ không đi quá sâu để giải thích định nghĩa về state và props, các bạn có thể tham khảo tại đây để biết thêm.
Unmounting, quá trình này diễn ra trước khi component của chúng ta bị xóa khỏi DOM. Để dễ hình dung hơn mình xin lấy một ví dụ như sau. render() { if (condition) return <Component/> else return null }. có thể thấy khi thỏa một điều kiện nào đó, ta sẽ return về một component, tức là component đấy đã được gắng lên cây DOM, nhưng sau đó vì một lý do nào đó mà ta lại return NULL => lúc này component khi nãy của chúng ta đã bị thay thế bởi NULL, thế lúc này nó đi đâu ?... Bạn nghĩ đúng rồi đấy, nó đi về với cát bụi, bị xóa sổ khỏi trần đời, và trước khi nó chính thức bị xóa sổ nó muốn gửi tới bạn vài lời trăng trối, thì lời trăng trối này được gửi ở giai đoạn Unmounting.
Hình ảnh tham khảo từ: https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
Hình ảnh tham khảo từ: https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

3. React Hook thật tuyệt vời, nhưng cũng đầy cạm bẫy.

Với những người mới học, thì gần như cái đầu tiên mà họ tiếp xúc sẽ là hook, chứ chẳng phải cái mớ bồng bông life cycle với cả đống methods trong từng giai đoạn kia, còn với những người đã làm việc lâu năm với React (phiên bản Class), thì hook như một vị cứu tin vậy, đơn giản, ngắn gọn, thật là sexy...
Mình sẽ lấy một vài hook cơ bản thông dụng mọi người hay xài để dễ hình dung hơn. Đầu tiên thông dụng nhất có lẽ sẽ là: useState useEffect cặp đôi hoàn hảo, cập bài trùng và có lẽ cũng là thứ dễ bị hiểu một cách mơ hồ nhất. Mình sẽ lấy một đoạn code để dễ hình dung hơn. Khi click button bạn sẽ thấy biến counter được cập nhật lại trên giao diện, đồng thời sau đó giá trị mới của counter cũng được in ra ở màng hình console qua dòng code bên trong useEffect.
Example useState and useEffect
Example useState and useEffect
Example useState and useEffect
Example useState and useEffect
Và rồi từ đây mọi người sẽ hay bảo nhau rằng, useEffect có 2 tham số, tham số đầu tiên là 1 hàm callback, tham số thứ 2 là một danh sách các dependencies, mỗi khi có một dependency nào đó đó bị thay đổi thì hàm callback bên trong useEffect sẽ được chạy lại. thoạt nghe thì có vẻ đúng, nhưng nếu suy nghĩ kỹ thì sẽ thấy hình như có điều gì đó sai sai, có chắc là "mỗi khi một dependency nào đó bị thay đổi thì useEffect sẽ chạy lại ?", vậy sẽ như nào nếu như tôi tạo một biến nào đấy ở bên ngoài component, và khi click button tôi sẽ cập nhật lại giá trị của biến đấy ? ví dụ: let counter = 0; function App { ... useEffect(() => { console.log('New Counter | ', counter) },[counter]); return ( <button onClick={() => counter += 1}> Click me to increase counter </button> ) }
Bạn hãy thử chỉnh sửa lại đoạn code ban đầu thành như ví dụ trên xem như thế nào. useEffect có chạy lại không ?. Rõ ràng là không, mặt dù chúng ta vẫn truyền biến counter vào làm dependency cho useEffect 🤔. Okay giờ hãy thử chỉnh sửa đoạn code trên một chút thành như thế này. let counter = 0; function App(){ const [_, forceRender] = useState({}); ... <button onClick={() => { counter += 1; forceRender({}) }} }
Bây giờ thử lại xem nào... có phải useEffect đã thật sự chạy lại ?. Vậy điều ma thuật gì đã diễn ra 😳 ?. Câu trả lời chính là, không phải dependencies thay đổi sẽ làm useEffect chạy lại, mà chính là sau mỗi lần re-render useEffect sẽ kiểm tra xem dependencies có bị thay đổi hay không, nếu có thì nó sẽ thực thi hàm callback bên trong nó, ngược lại thì không. Điều này cũng diễn ra tương tự với một số hook khác như: useCallback, useMemo... từ đây chúng ta có thể thấy rằng việc chúng ta sử dụng React mà không thực sự tìm hiểu rõ về nó, sẽ dễ dẫn đến những hiểu lầm. Mình thừa nhận trong thực tế thì hiếm khi hoặc thậm chí là chẳng có ai lại đi lưu state vào một biến ở ngoài component rồi lại cheat cho component re-render để cập nhật lại giao diện cả... nhưng nếu chúng ta không nắm rõ bản chất của React thì có lẽ ta sẽ không biết, hơn thế nữa, một số thư viện state management, một số phương pháp để optimize render performance cũng thật sự cần phải hiểu rõ điều trên. Nếu bài viết này được nhiều người quan tâm, có thể mình sẽ làm tiếp một bài viết, tự viết một state management giống Redux, cũng như nêu ra cách mà nó optimize được performance. Nếu đã đọc đến đây thì xin cảm ơn bạn rất nhiều vì đã lắng nghe những chia sẽ của mình, trên đây chỉ là cách hiểu dựa trên sự tự tìm hiểu và trãi nghiệm của cá nhân, nên rất mong được mọi người góp ý để mình có thêm nhiều góc nhìn tốt hơn, cũng như góp phần nâng cao kiến thức cho mọi người. Thank you and see yah.