+2

[Javascript] Concept cần nắm vững trước khi học React | part 2

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 declarationshoisted. Đâ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/expressionsarrow 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 functionslexical 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 foodrender 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-basedglobal 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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí