[Javascript] Concept cần nắm vững trước khi học React | part 2
Bài đăng này đã không được cập nhật trong 3 năm
Trong bài viết trước, mình đã giới thiệu một số concept mà chúng ta phải nằm lòng trước khi thực sự muốn bắt đầu làm việc vởi React
. Đơn giản chỉ là những cú pháp như toán tử 3 ngôi, destructuring
hay spread operator
nhưng cũng phần nào cải thiện chất lượng code cùng như rút ngắn thời gian xử lý cho chúng ta. Ở phần này, chúng ta hãy cùng nhau tìm hiểu nốt những phần còn lại về việc thao tác, xứ lý với các function nhé.
Function declarations, function expressions, and arrow functions
Một function declaration sẽ có dạng đơn giản như sau:
function getFood(index) {
const food = ["Pancakes", "cake", "Hamburger", "French fries", "Pizza"];
return food[index];
}
Ngược lại, đây là một function expression:
const getFood = function(index) {
const food = ["Pancakes", "cake", "Hamburger", "French fries", "Pizza"];
return food[index];
}
Và một arrow function expression được tạo ra để thay thế cho hai function kia. Lợi thế của nó là ở cú pháp, cho phép bạn viết các function theo cách ngắn gọn hơn.
const getFood = (index) => {
const food = ["Pancakes", "cake", "Hamburger", "French fries", "Pizza"];
return food[index];
}
Nếu chỉ có một tham số, chúng ta có thể bỏ qua dấu ngoặc đơn đi:
const getFood = index => {
const food = ["Pancakes", "cake", "Hamburger", "French fries", "Pizza"];
return food[index];
}
Nếu function của bạn không cần truyền bất kỳ tham số nào, hãy cần sử dụng dấu ngoặc đơn:
const getFood = () => {
return ["Pancakes", "cake", "Hamburger", "French fries", "Pizza"];
}
Nếu thân hàm bao gồm một câu lệnh return
duy nhất, bạn có thể thay thế bằng một lệnh return
ngầm định, như sau:
;
const getFood = index => ["Pancakes", "cake", "Hamburger", "French fries", "Pizza"][index];
Chỉ với function declarations
, chúng ta mới có thể gọi các hàm trước khi chúng được định nghĩa. Bởi vì function declarations
là hoisted. Đây là một cơ chế của javascript, cho phép các khai báo biến hoặc hàm được dời lên trên đầu phạm vi của chúng trước khi thực thi đoạn code
console.log(getFoodDeclaration()); // "French fries"
console.log(getFoodExp()); // ReferenceError: Cannot access 'getFoodExp' before initialization
console.log(getFoodArrow()); // ReferenceError: Cannot access 'getFoodArrow' before initialization
function getFoodDeclaration() {
return "French fries";
}
const getFoodExp = () => {
return "Hamburger";
}
const getFoodArrow = () => "Pizza";
Còn một điểm khác biệt khác giữa function declarations/expressions
và arrow function expression
đó là this
. Thứ mà chúng ta sẽ nêu ở phần Classes
.
Classes
Một class sẽ đại diện cho một kế hoạch chi tiết cho các new object
. Các biến và hàm có thể được gắn vào một class
và được gọi là thuộc tính và phương thức tương ứng. Trong class
, this sẽ refers đến current instance. Nếu bạn đã có chút base về hướng đối tượng(của java chẳng hạn). Nhiều khả năng bạn sẽ có những nhận định không chính xác về nó. Bởi đơn giản javascript sẽ không chặt chẽ như java
Một lớp có thể có một phương thức khởi tạo, đại diện cho một hoặc nhiều các phương thức dùng để khởi tạo các đối tượng mới.
Có thể khởi tạo lớp bằng từ khóa new.
Với điều này, hàm khởi tạo được gọi và một đối tượng mới được tạo.
class Fruit {
// class body
constructor() {
// property
this.popular = "🥝"
}
whatsPopular() {
// method body
console.log(this.popular) "🥝"
}
}
// instantiate an object from the class
const fruit = new Fruit();
// call the method on the instance
fruit.whatsPopular();
Một khái niệm quan trọng khác là inheritance with the class syntax. Bằng cách sử dụng từ khóa super.
class Food {
constructor() {
this.popular = "Pizza"
}
}
class Fruit extends Food {
constructor() {
// required to call constructor of parent class
// needs to be first statement in child constructor
super();
// override
this.popular = "🥝"
}
whatsPopular() {
console.log(this.popular) // "🥝"
}
}
const fruit = new Fruit();
fruit.whatsPopular();
Với ES2017, cú pháp để sử dụng các thuộc tính và phương thức của lớp là một little bit more concise. Chúng ta cũng có thể sử dụng arrow function
làm phương thức.
class Food {
popular = "🍕";
}
class Fruit extends Food {
popular = "🥝";
// method by arrow function expression
whatsPopular = () => {
console.log(this.popular)
};
}
const fruit = new Fruit();
fruit.whatsPopular();
Thông thường, khi bạn gặp các lớp trong phát triển React, bạn sẽ tìm thấy cú pháp ES2017.
// class-based React components must extend React.Component (or a subclass like React.PureComponent)
class Food extends React.Component {
// define default React state with ES2017 property syntax
state = {
popular = "Pizza"
}
render() {
// required method by every class-based React component
}
}
Lưu ý rằng phần này không có nghĩa là giải thích đầy đủ về các lớp JavaScript. Bạn không cần phải dành quá nhiều thời gian cho việc học các lớp nếu kế hoạch của bạn là học React.
Về cơ bản, chúng ta chỉ cần hiểu cơ bản về các lớp như được trình bày ở đây để có thể đọc và hiểu class-based React components
.
Hãy cùng nhau xem xét ví dụ đầu tiên nhé, yêu cầu một bind
; nếu không, lệnh gọi this.setState
gây ra lỗi
class Button extends React.Component {
constructor(props) {
super(props);
this.state = { clicked: false };
// this.onClick = this.onClick.bind(this);
}
onClick() {
this.setState({ clicked: true }); // ERROR this.setState is not a function
}
render() {
return (
<div>
<p>{this.state.clicked && "clicked"}</p>
<button onClick={this.onClick}>click</button>
</div>
);
}
}
Với cú pháp ES2017
, chúng ta có thể viết các class-based components dễ hiểu hơn. Lý do là vì các arrow functions
có lexical this, và giá trị của nó trong các arrow functions
được xác định bởi phạm vi xung quanh.
class Button extends React.Component {
state = {
clicked: false
}
onClick = () => {
this.setState({ clicked: true });
}
render() {
return (
<div>
<p>{this.state.clicked && "clicked"}</p>
<button onClick={this.onClick}>click</button>
</div>
);
}
}
Array functions
Thành thạo array functions là một kỹ năng quan trọng đối với React developers
. map() được sử dụng cơ bản trong mọi ứng dụng React application
, ví dụ như để lặp lại danh sách các đối tượng food
và render
chúng ra trong từng thẻ li
cơ bản.
Hàm map ()
tạo ra một mảng mới có cùng số phần tử. Tuy nhiên, đối với mỗi phần tử ban đầu, một phép toán đã được áp dụng để tạo ra các phần tử mới. Ví dụ sau tạo một mảng mới với các loại trái cây trùng lặp cho từng phần tử`.
const fruits = ["🍓", "🥝", "🍌", "🍒"];
const moreFruits = fruits.map(f => `${f}${f}`);
console.log(moreFruits); // ["🍓🍓", "🥝🥝", "🍌🍌", "🍒🍒"]
filter() thường được sử dụng với việc quản lý state
vì nó trả về một mảng hoàn toàn mới chỉ chứa những phần tử của mảng ban đầu đã thỏa mãn một điều kiện đã cho.
const people = [
{ name: "Quynh", sex: "female" },
{ name: "Teo", sex: "male" },
{ name: "Dung", sex: "female" }
];
const women = people.filter(person => person.sex === "female");
console.log(women); /* [{ name: "Quynh", sex: "female" }, { name: "Dung", sex: "female"}] */..
findIndex() trả về index
của phần tử đầu tiên tìm thấy; nếu không, nó trả về -1
const fruits = ["🍓", "🥝", "🍒", "🍌", "🍒"];
console.log(fruits.findIndex(fruit => fruit === "🥝")); // 1
console.log(fruits.findIndex(fruit => fruit === "🍌🍌")); // -1
console.log(fruits.findIndex(fruit => fruit === "🍒")); // 2 (first match)
find() trả về phần tử đầu tiên thỏa mãn điều kiện tìm kiếm. Hàm này rất hữu ích trong việc quản lý state
của React
.
Ví dụ, chúng ta có một danh sách user. Chúng ta đã click
vào một user cụ thể trong danh sách và muốn hiển thị modal dialog
showing ra thông tin của user này.
const users = [
{ id: 1, name: "Teo", sex: "male" },
{ id: 2, name: "Quynh", sex: "female" },
{ id: 3, name: "Dung", sex: "female" }
];
function logUserInfo(id) {
console.log(users.filter(user => user.id === id));
}
logUserInfo(2); // { id: 2, name: "Quynh", sex: "female" }
Immutable vs. mutable values
Đây là concept tương đối là quan trọng do đó chúng ta cần hiểu kỹ về nó.
Immutable values and objects
- Giá trị và đối tượng bất biến không thể thay đổi sau đó. Do đó bản gốc sẽ không bị ảnh hưởng.
Primitive values
- Các giá trị nguyên thủy như chuỗi hoặc số là immutable
- bất biến.
Mặt khác, Objects
- các đối tượng là mutable
- có thể thay đổi được giá trị.
Hãy xem ví dụ sau có gì nha:
// immutable use case
// Change strings won't work. Throws error in strict mode
"use strict";
const hello = "world";
hello[0] = "W"; // try to upper case the first char
console.log(hello); // world (in none-strict mode)
Nếu có mindset không đúng về mutability
thì có thể gây ra bugs
.
// mutability use case
const meal = {
kind: "🍕",
origin: {
country: "Italy"
}
}
const fruit = {
kind: "🍇",
origin: meal.origin
};
console.log(`${fruit.kind} from ${fruit.origin.country}`); // ✅ "🍇 from Italy"
console.log(`${meal.kind} from ${meal.origin.country}`); // ✅ "🍕 from Italy"
// we bought new grapes from Germany
fruit.origin.country = "Germany";
console.log(`${fruit.kind} from ${fruit.origin.country}`); // ✅ "🍇 from Germany"
// we have caused an unwanted side effect
console.log(`${meal.kind} from ${meal.origin.country}`); // ❌ "🍕 from Germany"
Object
thì có thể thay đổi nhưng bạn có thể sử dụng Object.freeze() hoặc các thư viện của bên thứ ba như Immutable.js
để biến chúng thành bất biến(immutable
).
React team
khuyên bạn nên sử dụng các đối tượng bất biến trong hầu hết các ứng dụng của mình, ví dụ với component-based và global state.
Trong phát triển React, đừng cố gắng thay đổi trực tiếp state
. Ví dụ: đoạn mã sau đây cho biết cách bạn nên và cách bạn không nên cập nhật local state
của class-based React component
.
class Button extends React.Component {
state = {
clicked: false
}
onClick = () => {
// ❌ don't do this
this.state.clicked = true;
// ✅ instead do this: pass a new object to setState
this.setState({ clicked: true });
}
render() {
return (
<div>
<p>{this.state.clicked && "clicked"}</p>
<button onClick={this.onClick}>click</button>
</div>
);
}
}
Nếu bạn sử dụng global state management
như useReducer
hoặc Redux
, bạn nên cập nhật state
như thế này:
const newState = {
...state, // creates a copy of the current state
darkMode: action.darkMode, // just override to reflect the changes
};
Callback functions
Một hàm được truyền dưới dạng đối số cho một hàm khác được gọi là callback nếu hàm gọi đối số sau đó.
setTimeout nhận một callback function
làm đối số đầu tiên được gọi sau khi bộ đếm thời gian đã hết hạn (đối số thứ hai).
window.setTimeout(() => console.log("I'm a callback function"), 1000);
// after 1s: "I'm a callback function"
Trong React
, callback functions lại thường được dùng như props cho React components
. Do đó, child components
có thể thực thi các callbacks
được chuyển vào sau đó theo cách mà các parent component
có thể react
với nó (ví dụ: update state , update UI).
Callback functions
rất quan trọng trong React Hooks, trigger side effects với useEffect.
Kết luận
Vì quá trình phát triển React chủ yếu bao gồm việc viết mã JavaScript
đơn giản, do đó chúng ta nên tìm hiểu kỹ các nguyên tắc cơ bản về JavaScript
trước khi học React
. Với những tóm lược trên, mình mong phần nào giúp được bạn sẽ có một khởi đầu suôn sẻ hơn rất nhiều trên con đường làm việc với React
.
All rights reserved