Những chuỗi ngày cận Tết, quây quần bên gia đình, bên nồi bánh xanh đỏ lửa, nghi ngút khói mà thấy hạnh phúc vô cùng. Lại còn hì hục nướng củ khoai, bắp ngô, mặt đen nhẻm mà rộn ràng tiếng nói cười. 😽😽
Ngẫm lại thì, vèo cái đã hết một năm. Cảm ơn thật nhiều những người thân ở bên cạnh, những người anh chị em tuyệt vời luôn sát cánh và những trải nghiệm thú vị trong năm qua.
Chia tay năm cũ, chào đón năm Nhâm Dần với những thử thách và mục tiêu mới. Tết năm nay thì đúng vị rồi, rét chút và có tí mưa xuân nữa 💦💦
Chúc cho tất cả chúng ta bắt đầu một năm Nhâm Dần 2022 với nhiều điều thật mới mẻ, thật thành công, thật bình yên và thật vui tươi nhé
◼ Đặt vấn đề
Từ ngay đầu tháng qua, chú Đen đã dặn dò các Đồng âm:
- Mang tiền về cho Mẹ, đừng mang ưu phiền về cho mẹ ~~
Chú Đen mang tiền về cho mẹ thì rõ ràng rồi. Hưởng ứng chia sẻ của chú, chị gái ngồi cạnh mang quà về cho mẹ, anh trai phía sau mang con dâu về cho mẹ, genZ em kề cạnh thì nhất quyết mang khẩu trang về cho mẹ, blabla... 😸😸
Quay trở lại với ReactJS
của chúng ta, không biết JSX
nhà mình mang lại điều gì cho chiếc thư viện này nhỉ? Điều gì thật sự diễn ra sau những đoạn mã JSX
đó...(suynghiii)
Khởi động năm mới với chiếc chủ đề nhẹ nhàng này cùng mình nhé 😽😽))
■ Đối tượng
Bài viết chủ yếu hướng tới các bạn đã và đang tìm hiểu về ReactJS, nhưng trong quá trình tiếp cận JSX
còn băn khoăn cũng như muốn có cái nhìn rõ nét hơn về nó.
Trong bài viết này, chúng ta sẽ cùng nhau điểm qua 03 phần
:
🔗 Overview - Tổng quan JSX
🔗 Behind the scene - Những gì diễn ra phía sau JSX
🔗 Common issues - Một số vấn đề thường gặp với JSX
Đầu tiên, cùng nhau xem lại JSX
một chút! 😺😺
■ Overview
Theo Trang chủ:
JSX
isa syntax extension
toJavaScript
.
Là cú-pháp-mở-rộng của JavaScript
, JSX
cho phép chúng ta mô tả DOM tree
của ứng dụng một cách ngắn gọn và quen thuộc.
Như ngay chính JSX Spec của React team
cũng đã đề cập:
(JSX)…to define
a concise and familiar syntax
for definingtree structures
withattributes
.
Và cũng chính vì quá đỗi gần gũi như vậy, dẫn đến một-hiểu-lầm-khá-phổ-biến với các bạn tiếp cận với React
rằng: JSX
là HTML
.
Thoáng nhìn qua, cứ ngỡ một biến JavaScript
lại được gán giá trị là một thẻ HTML
😹😹:
const blogTitle = <h1>Make It Awesome</h1>;
Một điều lưu ý, JSX
không phải là kiểu JavaScript
tương thích với trình duyệt. Nghiễm nhiên, trình duyệt của không thể hiểu được đoạn mã này 😵😵. Đó là lý do chúng ta cần có Babel
.
Nói qua chút thì Babel
là một JavaScript compiler/transpiler
giúp chúng ta dịch đoạn JSX
trên thành JavaScript thuần
(có thể ghé đây nếu muốn tìm hiểu thêm nha) - đến đây thì quá là easy game
cho trình duyệt rồi 😽😽
Quá trình chuyển đổi này diễn ra trong build process
, do đó, trình duyệt sẽ không biết được sự có mặt của JSX
ngay từ đầu, thứ nó nhận được là một object
mô tả cấu trúc của ứng dụng 😸😸.
Hơn nữa, với một vài tính năng mới của ECMAScript 6
, một số trình duyệt cũ hơn vẫn chưa hiểu được, do đó, vẫn cần có Babel compiler
.
Giờ thì cùng xem thử Babel
làm như nào để được object
đó nhé (go).
■ Behind the scene
■ JSX to JS
Với component Blog
:
const Blog = () => (<h1>Make It Awesome</h1>);
sẽ được dịch thành:
const Blog = () => React.createElement(
'h1',
null,
'Make It Awesome'
);
Dĩ nhiên, khi ta viết thẳng React.createElement()
mà không dùng JSX
thì component Blog
vẫn cho cùng một kết quả tương tự.
Song, với một đoạn mã nhỏ xíu như trên thì việc dùng JSX
hay React.createElement()
đều ổn hết.
Trải nghiệm với React.createElement()
sẽ kém cạnh hơn khi components
có logic
quá phức tạp hoặc các elements
lồng nhau đa cấp. Trông khá khó đọc và khó bảo trì. Có lẽ JSX
sẽ nâng cao chất lượng trải nghiệm của dev
hơn - mang theo sức mạnh của HTML
và JavaScript
.
Để xem thêm cách chuyển đổi từ JSX sang React.createElement() như thế nào, bạn có thể ghé qua đây thử với các đoạn JSX cụ thể nha. 😉😉
■ React.createElement() API
Cú pháp của React.createElement() API
:
React.createElement(type, [props], [...children]);
Cùng xem qua các tham số của API này
:
type
: có thể làHTML tag
(div
,h1
, etc.),React fragment
hoặcReact component
củaelement
.props
:null
hoặc một đối tượng chứa các thuộc tính củaelement
.children
: là cácHTML tags
hoặcReact components
con củaelement
; trường hợp có nhiềuchildren
, sử dụngarray
React.createElement
sẽ tạo ra một object
đại diện cho element
đó:
{
type: 'h1',
props: {
children: 'Make It Awesome',
...
}
...
}
Chúng ta có thể xem chi tiết các property
của object này
bằng cách log
ra cửa sổ Console
:
const Blog = () => {
const blogTitle = <h1>Make It Awesome</h1>;
console.log(blogTitle);
return blogTitle;
};
Kết quả cho ra một plain JavaScript object
- được gọi là React element
- mô tả chính xác những gì được hiển thị lên màn hình.
FYI, có thể nói, React elements
- đại diện cho HTML elements
(nằm trên Original DOM
) - nằm trên Virtual DOM
. React
dựa vào các React elements
để tạo HTML elements
trên Virtual DOM
; Sau đó đồng bộ hóa với Original DOM
. Chi tiết về Original DOM
, Virtual DOM
, bạn có thể đọc qua bài viết này tại đây nhé!
Quay lại ví dụ của chúng ta, thêm chút props
và event
:
const dropByMyBlog = () => { /* ... */ };
const blogTitle = (
<h1 id="blogTitle" onClick={dropByMyBlog}>Make It Awesome</h1>
);
sẽ được dịch thành:
React.createElement(
'h1',
{
id: "blogTitle",
onClick: function() { /* ... */ }
},
"Make It Awesome"
);
Bạn đoán xem: Object
được tạo ra có dạng như thế nào? Thử và kiểm tra lại kết quả trên Code Sandbox nhé 😸😸
Giờ thì chúng ta cùng nhau đi qua một số vấn đề xoay quanh JSX
(go)
■ Common issues
■ Adjacent JSX-s
Cập nhật chút component Blog
:
const Blog = () => (
<h1>Make It Awesome</h1>
<p>URL: https://haodev.wordpress.com</p>
);
thì gặp ngay:
SyntaxError: Adjacent JSX elements must be wrapped in closing tag.
Lý do thì như thông báo rồi, cơ mà có bao giờ bạn tự hỏi tại sao lại như vậy!?! 🤔🤔
Điều này cũng dễ hiểu thôi. Ở phần trước, chúng ta đã biết JSX
được dịch sang React.createElement()
, hàm này tạo ra một object
tương ứng, được trả về bởi component
. Mà bản chất component
cũng chỉ là một function
. Function
trong JavaScript
thì chỉ trả về 1 giá trị thôi.
Do đó, component
không thể nào mà trả về 02 đối tượng ngang nhau được:
const Blog = () => (
React.createElement('h1', null, 'Make It Awesome'); React.createElement('p', null, 'URL: https://haodev.wordpress.com');
);
Để xử lý lỗi này, chúng ta có thể có 3 lựa chọn:
- Div Tag
- React Fragment
- Array Converter
Hai cách đầu có lẽ cũng không xa lạ gì:
const Blog = () => (
<div>
<h1>Make It Awesome</h1>
<p>URL: https://haodev.wordpress.com</p>
</div>
);
hay
const Blog = () => (
<>
<h1>Make It Awesome</h1>
<p>URL: https://haodev.wordpress.com</p>
</>
);
Cách dùng mảng thì có lẽ hơi ít gặp một chút:
const Blog = () => (
[<h1 key="blogTitle">Make It Awesome</h1>, <p key="blogURL">URL: https://haodev.wordpress.com</p>]
);
Sở dĩ chúng ta có thể làm như vậy vì trong JSX
, một đoạn <p>{[1, 2, 3, 4]}</p>
sẽ được chuyển thành <p>{1}{2}{3}{4}</p>
, 1234
vẫn được in ra bình thường không vấn đề gì 😸😸.
Chú ý thêm unique key
cho mỗi phần tử trong mảng để tránh Warning
nha ^^
Việc tại sao cần dùng key, bạn có thể tìm hiểu thêm trong bài viết này.^^
Trong 03
hướng xử lý trên, mình hay dùng React.Fragment
dạng shorthand
để bọc các Adjacent JSX-s
. Với trường hợp cần thêm các properties
(như key
trong vòng lặp chẳng hạn), mình chuyển qua dạng đầy đủ:
[1, 2, 3].map(num => (
<React.Fragment key={num}>
<h1>Make It Awesome</h1>
<p>URL: https://haodev.wordpress.com</p>
</React.Fragment>
));
React.Fragment
được thêm vào trong React version 16.2
để tránh việc chúng ta phải dùng Div tag
trong các trường hợp không cần thiết (không có ý nghĩa trong DOM Structure
hoặc chỉ có mục đích để tránh lỗi trên), một số khác cũng sẽ ảnh hưởng tới style
ứng dụng (flexbox
chẳng hạn). Với hướng Array Converter thì hơi khó nhìn và đòi hỏi việc kiểm soát key
.
Chốt lại thì React.Fragment
quá ư là ổn rồi đúng không nào ^^
Fragments
group a list of childrenwithout adding extra nodes
to theDOM
.
■ Class vs. className
Chúng ta có thể thêm các thuộc tính vào các JSX elements
:
<h1 id="blogTitle" className="blog-title">Make It Awesome</h1>
Đợt nọ, sau seminar React Fundamentals
nội bộ, một anh giáo Laravel đố mình, tại sao chúng ta phải dùng className
thay vì class
!?!
Hmm...
Thử xem saooo...
Mạnh dạn sửa thành class="blog-title"
nhận ngay chiếc
Warning: Invalid DOM property 'class'.
Tại sao nhỉ? (thinking)
Theo MDN:
The name
className
is used forclass property
instead ofclass
because of conflicts with theclass
keyword in many languages which are used to manipulate the DOM.
Chính vì vậy, việc truy cập props.class
dẫn đến Warning
như trên 😸😸.
Ngoài ra, bạn có thể ghé Quora để tham khảo thêm câu trả lời siêu có tâm của chị Sophie Alpert
- cựu React team manager
tại Facebook
nha, quá là uy tín luônn =))
■ import React
Khi làm việc với các components
, bạn có bao giờ để ý đoạn import
quen thuộc này:
import React from 'react';
Giả sử như component
phía dưới chỉ trả về đoạn JSX
đơn giản; và cũng không sử dụng các APIs
khác trong thư viện 'react'
như useState()
, useEffect()
, etc.
Giờ thì xóa dòng này điii =))
Thử đoán xem kết quả sẽ như thế nào nhé...
Hai trường hợp có thể xảy ra:
- Hoặc vẫn chạy ngon lành
- Hoặc thông báo lỗi trong
build process
.
Vậy, lý do thật sự cho điều này là gì?
Ở phần trên, chúng ta cũng đã biết JSX
sẽ được compile
ra React.createElement()
rồi đúng không nào.
When compiled, it calls the
React.createElement()
function. So we need to haveReact
in scope forJavaScript
to know-what-to-do-with-the-compiled-code.
Khi chúng ta sử dụng JSX
, Babel compiler
sẽ chuyển chúng thành các lệnh gọi hàm React.createElement()
. Do đó, chúng ta cần có React
trong scope
hoạt động của JSX
(buộc cần import React from 'react';
).
Cho tới bản React 17.0
, Facebook team
đã "collaborate"
với Babel
, JSX
with new transform không còn yêu cầu điều này nữa. 😽😽
It is possible to write
JSX
without importing theReact
library at the top level or havingReact
in scope.
// Inserted by a compiler (don't import it yourself!)
import {jsx as _jsx} from 'react/jsx-runtime';
const Blog = () => _jsx('h1', { children: 'Make It Awesome' });
■ Kết
Yayyy... Như vậy là chúng ta đã cùng nhau tìm hiểu về JSX
và vài điều thú vị diễn ra xung quanh nó rồi.
Hy vọng rằng bài viết này có thể giúp ích được các bạn đã và đang tiếp cận với ReactJS
. Có thể nói rằng, JSX
chẳng phải từ khóa khó tiếp cận với người mới, ấy thế nhưng, cùng nhau tìm hiểu thêm chút để giải thích Tại sao thế này, Tại sao thế kia? để nắm được bản chất thì cũng thú vị nhỉ ^^
Cảm ơn các bạn đã đọc bài chia sẻ này. Đầu xuân năm mới, lì xì mình 1 upvote
để có thêm động lực cho những bài viết sắp tới nhé 😺😺
Và trong thời điểm hiện tại thì...
Mặc dù thời gian này (thời điểm mình publish
bài viết, 01/02/2022), các tỉnh đã có các biện pháp phòng tránh, kiểm soát tình hình dịch bệnh; Việc tiêm vaccine Covid-19
cũng đã được triển khai, song, đang trong thời gian nghỉ Tết Âm lịch nên nhu cầu di chuyển của người dân là không thể tránh khỏi, chúng ta cũng chưa thể chủ quan, hãy tiếp tục tuân thủ quy tắc 5K
được Bộ Y tế
khuyến cáo:
#Coronavirus #5K #BoY Te
Khẩu trang - Khử khuẩn - Khoảng cách - Không tập trung - Khai báo y tế
để có thể giữ an toàn cho bản thân và mọi người xung quanh 😺😺
Một lần nữa, chúc các bạn kì nghỉ Tết ấm áp, đong đầy hạnh phúc bên gia đình nhé!
Năm mới ghé qua nhà mình "thưởng bánh uống trà" chút rồi về ^^
■ Credits
- Resources: React document, Yogesh Chavan's post via Freecodecamp, Ryan Harris's post via Freecodecamp, Ifeoma Imoh's post via Progress Telerik.
- Thumbnail: Đen - Mang Tiền Về Cho Mẹ ft. Nguyên Thảo (M/V), cuongkhung1993 via Reddit.
- Policies:
- This original article from My Make It Awesome blog.
- Use my contents for sharing purpose, please attached resource linked to my blog.
- Use my contents for trading purpose, please contact me.
- Support: Buy me a pizza.
- Copyright: The posts in a spirit of sharing knowledge. If there is anything with regard to copyright of your contents, please contact me.
Happy coding!