ES6 thiết yếu cho React: let, const, arrow function, destructuring, spread
Đây là bài mở đầu của khóa. React được viết bằng JavaScript hiện đại (ES6+), nên gần như mọi dòng code React bạn gặp sau này đều dựa trên 5 thứ trong bài này: let/const, arrow function, template literal, destructuring và spread. Bạn đã biết HTML/CSS cơ bản rồi, nên ta chỉ cần thêm đúng phần JavaScript này là đủ để đọc và viết component.
Sau bài này, bạn sẽ:
- Phân biệt
letvàconst, biết chọn cái nào theo mặc định - Viết arrow function và tránh bẫy
thiscũng như bẫy trả về object - Dùng template literal thay cho nối chuỗi bằng dấu
+ - Bóc dữ liệu bằng destructuring object và array
- Dùng spread để tạo bản sao thay vì sửa trực tiếp dữ liệu gốc
let và const: mặc định là const, chỉ đổi khi thật cần
Hãy nghĩ tới hai chiếc hộp dán nhãn. const là hộp dán băng keo: nhãn không đổi được. let là hộp dán nhãn bằng keo giấy: đổi nhãn lúc nào cũng được. Còn var là chiếc hộp cũ rò rỉ ra ngoài mọi ngăn kéo, ta bỏ hẳn.
const tenApp = "Quản lý chi tiêu";
// tenApp = "Khác"; // TypeError: Assignment to constant variable
let soDu = 0;
soDu = 500000; // OK
Điểm quan trọng nhất, và cũng là chỗ người mới hiểu sai nhiều nhất:
const user = { ten: "Lan", tuoi: 20 };
user.tuoi = 21; // OK! const không đóng băng nội dung object
// user = {}; // Lỗi: gán lại biến thì không được
| Khai báo | Gán lại biến | Phạm vi | Dùng khi nào |
|---|---|---|---|
var |
được | hàm | tránh trong code mới |
let |
được | block | biến đếm, biến cộng dồn, giá trị thật sự thay đổi |
const |
không | block | mặc định cho hầu hết khai báo |
flowchart TD
Start["Cần khai báo biến"] --> Q{"Giá trị có bị gán lại?"}
Q -->|"Không"| Const["📌<br/>const"]
Q -->|"Có"| Let["🔄<br/>let"]
Bài tập 1. Trong dự án quản lý chi tiêu, bạn cần một biến cộng dồn tổng tiền khi duyệt mảng các khoản chi. Dùng let hay const? Vì sao?
Xem gợi ý
Dùng let tong = 0; rồi tong += khoan.tien; trong vòng lặp, vì biến này bị gán lại nhiều lần. Mảng khoanChi thì dùng const.
Arrow function: ngắn hơn, và this không bị đổi
// Cách cũ
const chaoCu = function (ten) {
return "Xin chào " + ten;
};
// Arrow function
const chao = (ten) => `Xin chào ${ten}`;
Ba quy tắc viết gọn:
- Một tham số thì bỏ được ngoặc:
x => x * 2 - Thân hàm chỉ có một biểu thức thì bỏ được
returnvà{} - Trả về object literal thì phải bọc ngoặc tròn:
() => ({ id: 1 })
Điều thứ ba là bẫy kinh điển. Không có ngoặc tròn, JavaScript hiểu {} là thân hàm, nên hàm trả về undefined và bạn ngồi tìm lỗi cả buổi.
Về this: arrow function không có this riêng, nó dùng this của phạm vi bao ngoài. Với function component hiện đại bạn ít gặp chuyện này, nhưng đó là lý do các callback trong React luôn được viết bằng arrow function.
Bài tập 2. Viết lại hàm sau thành arrow function một dòng: function nhan2(x) { return x * 2; }
Xem đáp án
const nhan2 = (x) => x * 2;
Template literal: ghép chuỗi bằng backtick
Dấu backtick ` cho phép chèn biểu thức bằng ${} và viết chuỗi nhiều dòng mà không cần \n. Trong React, bạn dùng nó nhiều nhất ở hai chỗ: className động và URL API.
const dangChon = true;
const lop = `menu-item ${dangChon ? "active" : ""}`;
const API_BASE = "https://api.example.com";
const url = `${API_BASE}/todos/${id}`;
Bài tập 3. Từ hai biến ten = "Lan" và soViec = 3, tạo chuỗi Xin chào Lan, bạn có 3 việc cần làm.
Xem đáp án
const loi = `Xin chào ${ten}, bạn có ${soViec} việc cần làm`;
Destructuring: bóc dữ liệu ra khỏi object và array
const sanPham = { id: 1, ten: "Bàn phím", gia: 500000 };
const { ten, gia } = sanPham; // lấy đúng tên field
const { ten: tenSP, gia: giaSP } = sanPham; // đổi tên biến
const { ten = "Chưa có tên" } = sanPham; // giá trị mặc định khi field thiếu
const diem = [9, 8, 7];
const [dau, hai] = diem; // dau = 9, hai = 8
const [, , ba] = diem; // bỏ qua phần tử đầu: ba = 7
Đây chính là lý do bài này quan trọng với React. Hai dòng code bạn sẽ gặp ở mọi component:
const [count, setCount] = useState(0); // array destructuring
function TheSanPham({ ten, gia }) { // destructuring ngay trong tham số
return <div>{ten} - {gia}</div>;
}
Bài tập 4. Cho const nguoiDung = { hoTen: "Minh", email: "[email protected]" }. Viết một dòng destructuring lấy hoTen vào biến tên ten.
Xem đáp án
const { hoTen: ten } = nguoiDung;
Spread và rest: ... làm hai việc khác nhau
Spread trải giá trị ra, rest gom giá trị lại. Cùng một ký hiệu, khác ngữ cảnh.
// Spread: tạo bản sao
const cu = [1, 2, 3];
const moi = [...cu, 4]; // [1, 2, 3, 4]
const user = { ten: "Lan", tuoi: 20 };
const capNhat = { ...user, tuoi: 21 }; // ghi đè đúng một field
// Rest: gom phần còn lại
const { ten, ...phanConLai } = user;
function tong(...cacSo) {
return cacSo.reduce((a, b) => a + b, 0);
}
Trong React, spread là cách chuẩn để cập nhật state mà không sửa thẳng dữ liệu cũ. Mặc dù bài này chưa chạm tới useState, hãy nhớ sẵn hình mẫu setTodos([...todos, viecMoi]) và setUser({ ...user, ten: "Minh" }).
Lưu ý: spread chỉ là shallow copy. Object lồng bên trong vẫn dùng chung tham chiếu với object gốc.
Bài tập 5. Cho const hoaDon = { ma: "HD01", tong: 100, daThanhToan: false }. Viết code tạo object mới đã thanh toán mà không sửa hoaDon.
Xem đáp án
const daThanhToan = { ...hoaDon, daThanhToan: true };
Điền khuyết: kiểm tra nhanh cú pháp ES6
- Từ ES6, ta nên dùng _____ làm mặc định khi khai báo biến, chỉ dùng let khi biến bị gán lại.
- Arrow function không có _____ riêng, nó dùng của phạm vi bao ngoài.
const [count, setCount] = useState(0)là ví dụ của _____.- Để tạo bản sao object rồi ghi đè một field, ta viết
{ ...user, tuoi: 21 }, trong đó...là _____ operator. - Muốn nối giá trị biến vào chuỗi từ ES6, ta dùng _____ với dấu backtick.
const { ten: hoTen } = nguoilấy fieldtennhưng _____ biến thànhhoTen.
Thẻ ghi nhớ ES6
Front: const khác let ở điểm nào?
Back: const không cho gán lại biến, let thì cho phép.
Front: Vì sao nên mặc định dùng const?
Back: Vì nó báo lỗi ngay khi vô tình gán lại, giúp code an toàn hơn.
Front: const khai báo object thì có sửa được thuộc tính không?
Back: Có, const chỉ chặn gán lại biến chứ không đóng băng nội dung object.
Front: Arrow function khác function thường ở điểm cốt lõi nào?
Back: Arrow function không có this riêng, nó dùng this của phạm vi bao ngoài.
Front: Arrow function trả về object literal phải viết thế nào?
Back: Bọc object trong ngoặc tròn: () => ({ id: 1 }).
Front: Khi nào bỏ được ngoặc đơn tham số của arrow function?
Back: Khi chỉ có đúng một tham số, ví dụ x => x * 2.
Front: Template literal dùng cặp ký tự gì để bao chuỗi?
Back: Dấu backtick, và chèn biểu thức bằng ${...}.
Front: Object destructuring lấy field ten và đổi tên thành hoTen viết thế nào?
Back: const { ten: hoTen } = nguoi;
Front: Destructuring có hỗ trợ giá trị mặc định không?
Back: Có: const { ten = 'Khách' } = nguoi;
Front: const [count, setCount] = useState(0) dùng kỹ thuật gì?
Back: Array destructuring.
Front: Spread operator làm gì với object?
Back: Tạo bản sao nông và có thể ghi đè field: { ...user, ten: 'Minh' }.
Front: Vì sao React cần spread khi cập nhật state? Back: Vì không được mutate state trực tiếp, phải tạo object hoặc array mới.
Front: Rest khác spread ở đâu? Back: Rest gom nhiều giá trị còn lại vào một biến, spread trải giá trị ra.
Front: Spread có copy sâu object lồng nhau không? Back: Không, đó là shallow copy, object con vẫn chia sẻ tham chiếu.
Bốn lỗi ES6 hay gặp khi bắt đầu với React
- Sửa thẳng object thay vì tạo bản sao.
user.ten = "Minh"chạy được vớiconst, nhưng React sẽ không thấy dữ liệu đổi. Luôn dùng{ ...user, ten: "Minh" }. - Quên ngoặc tròn khi arrow function trả về object.
() => { id: 1 }trả vềundefined, phải là() => ({ id: 1 }). - Dùng
letcho mọi thứ. Thói quen này khiến biến bị gán lại mà không ai báo lỗi. Mặc địnhconst, chỉ nâng lênletkhi cần. - Quên rằng destructuring không tạo bản sao.
const { ten } = uservẫn trỏ tới cùng chuỗi, cònconst { diaChi } = userthìdiaChivẫn là object gốc củauser, sửa nó là sửa luônuser.
Mẹo nhanh: nếu bạn thấy dấu + đang nối chuỗi, gần như luôn có một template literal gọn hơn ở đó.
Mang theo gì khi sang bài map, filter, reduce
Năm điều cần nhớ: const là mặc định, let chỉ khi biến đổi giá trị; arrow function ngắn gọn nhưng phải bọc ngoặc tròn khi trả về object; template literal thay cho nối chuỗi; destructuring lấy đúng field bạn cần; spread tạo bản sao thay vì sửa dữ liệu gốc.
Bài tiếp theo, Hàm và mảng trong thực tế: map, filter, reduce, sẽ dùng chính arrow function và destructuring bạn vừa học để biến một mảng dữ liệu thô thành danh sách hiển thị được trên giao diện.