Menu

Class component trong React: state, vòng đời, chuyển đổi

Class component là một lớp JavaScript kế thừa React.Component và trả về JSX từ phương thức render. Tìm hiểu this.props, this.state và setState, bind trình xử lý sự kiện, các phương thức vòng đời, PureComponent và cách chuyển một class sang function component.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Class component là một lớp (class) JavaScript kế thừa React.Component và trả về JSX từ phương thức render(). Nó đọc props từ this.props, giữ state trong this.state và cập nhật state bằng this.setState(). Function component dùng hook đã thay thế chúng trong code mới, nhưng class vẫn được hỗ trợ đầy đủ, xuất hiện trong nhiều codebase cũ, và là cách duy nhất để viết một error boundary.

Mỗi bộ đếm giữ state riêng. Thêm một <Counter label="Plums" /> thứ ba, nó cũng bắt đầu từ 0.

Cấu tạo của một class component

import { Component } from 'react';

class Welcome extends Component {
    static defaultProps = { name: 'stranger' };

    constructor(props) {
        super(props); // required before using this
        this.state = { visits: 1 };
    }

    render() {
        return <h1>Hello, {this.props.name}. Visit #{this.state.visits}</h1>;
    }
}
  • Class kế thừa Component (hoặc React.Component khi dùng import React from 'react').
  • render() là bắt buộc. Nó phải thuần (pure), giống thân của một function component: đọc this.props và this.state, trả về JSX.
  • this.props chứa các props. Chúng là chỉ đọc.
  • State có thể được khởi tạo trong constructor (gọi super(props) trước) hoặc, phổ biến hơn ngày nay, dưới dạng class field: state = { visits: 1 };.
  • static defaultProps vẫn hoạt động với class trong React 19 (nó chỉ bị loại bỏ với function component).

this.setState gộp state, và có thể nhận hàm cập nhật

setState có hai điểm khác với hàm setter của useState. Nó gộp (merge) đối tượng bạn truyền vào state hiện tại, nên bạn chỉ cập nhật những khóa bạn nêu tên. Và, giống mọi lần cập nhật state trong React, nó không được áp dụng ngay, nên hãy dùng dạng hàm cập nhật (updater) khi giá trị tiếp theo phụ thuộc vào giá trị trước.

"+3 with objects" chỉ cộng một, vì cả ba lệnh gọi đều đọc cùng một this.state.likes. "+3 with updaters" cộng ba. "Move to Paris" giữ lại name và likes (nhờ việc gộp) nhưng mất zip, vì việc gộp chỉ sâu một cấp. Hãy viết address: { ...this.state.address, city: 'Paris' } để giữ nó lại. Đối số thứ hai (tùy chọn) của setState chạy sau khi cập nhật, và console hiển thị địa chỉ mới ở đó.

Bind trình xử lý sự kiện

Một phương thức được truyền dưới dạng onClick={this.handleClick} sau đó sẽ được gọi như một hàm thường, không kèm đối tượng của nó, nên this bên trong là undefined.

Nút đầu tiên ghi ra một TypeError về việc đọc setState của undefined. Nút thứ hai chạy được, vì arrow function trong class field giữ lại this khi instance được tạo. Cách sửa cũ hơn, vẫn hay gặp trong code legacy, là bind trong constructor:

constructor(props) {
    super(props);
    this.state = { on: false };
    this.handleClick = this.handleClick.bind(this);
}

Một arrow function viết inline, onClick={() => this.handleClick()}, cũng chạy được.

Các phương thức vòng đời

Class phản ứng với việc mount, cập nhật và unmount qua các phương thức có tên cố định. Những phương thức bạn sẽ gặp nhiều nhất:

  • componentDidMount(): sau khi lần render đầu tiên đã vào DOM. Bắt đầu timer, subscription, request.
  • componentDidUpdate(prevProps, prevState): sau mỗi lần cập nhật. Hãy so sánh với giá trị trước đó trước khi làm việc gì, nếu không bạn sẽ rơi vào vòng lặp.
  • componentWillUnmount(): trước khi bị gỡ bỏ. Hoàn tác những gì componentDidMount đã bắt đầu.
  • shouldComponentUpdate(nextProps, nextState): trả về false để bỏ qua một lần render.

Trang vòng đời component có danh sách đầy đủ, thứ tự chạy của chúng và một component ghi log từng phương thức.

Chuyển class sang function component

Đây là một class dùng cả ba phương thức vòng đời phổ biến. Nó tăng mỗi giây, ghi log khi prop label thay đổi và dừng timer khi bị gỡ bỏ.

Và đây là cùng component đó dưới dạng hàm. Mỗi phương thức vòng đời trở thành một phần của effect, và phần thiết lập cùng phần dọn dẹp liên quan nằm cạnh nhau thay vì tách ra hai phương thức.

Cả hai timer đếm giống nhau và ghi log mỗi khi label đổi. Bản hàm còn ghi log một lần khi mount, vì effect cũng chạy sau lần render đầu tiên. Các bước cho mọi lần chuyển đổi:

  1. this.props.x trở thành một prop được destructure, { x }.
  2. Mỗi khóa của this.state trở thành một useState riêng. Nhớ rằng setter thay thế giá trị chứ không gộp.
  3. Các phương thức trở thành hàm thường bên trong component. Không cần bind.
  4. componentDidMount, componentDidUpdate và componentWillUnmount trở thành các lệnh gọi useEffect, được nhóm theo việc chúng làm thay vì theo thời điểm chạy.
  5. createRef trở thành useRef, còn static contextType cùng this.context trở thành useContext hoặc use.

Hãy để error boundary ở dạng class; chúng không có phiên bản hàm tương đương.

PureComponent

Một class component thông thường render mỗi lần component cha render. PureComponent trước tiên so sánh props và state mới với lần trước, sâu một cấp, và bỏ qua lần render khi không có gì thay đổi. Đây là bản tương ứng dạng class của React.memo.

Bấm nút: chỉ PlainLabel rendered được ghi log, vì PureLabel nhận cùng một text. Truyền text={'hello ' + clicks} cho cả hai thì cả hai đều render mỗi lần bấm.

Phép so sánh là nông, nên một prop là đối tượng hoặc hàm mới sau mỗi lần cha render (style={{ color: 'red' }}, onClick={() => ...}) sẽ vô hiệu hóa nó. Đó cũng là cái bẫy giống như với React.memo.

Những thứ React 19 đã loại bỏ khỏi class

Nếu bạn bảo trì code class cũ, một vài mẫu không còn chạy trong React 19:

  • String ref (ref="input" cùng this.refs.input). Hãy dùng createRef() hoặc ref callback.
  • Legacy context (contextTypes, childContextTypes, getChildContext). Hãy dùng createContext với static contextType.
  • propTypes bị bỏ qua. Hãy dùng TypeScript hoặc tự viết phần kiểm tra.
  • findDOMNode đã bị xóa. Hãy gắn ref vào phần tử DOM thay thế.

Các phương thức cũ componentWillMount, componentWillReceiveProps và componentWillUpdate cũng là legacy. Chúng vẫn chạy, cả dưới tên cũ lẫn tên có tiền tố UNSAFE_ (UNSAFE_componentWillMount và các tên tương tự), nhưng đã bị đánh dấu deprecated và StrictMode sẽ cảnh báo về chúng; hãy chuyển code của chúng sang componentDidMount, componentDidUpdate hoặc getDerivedStateFromProps.

Câu hỏi thường gặp

Class component trong React có bị loại bỏ không?

Không. Chúng vẫn được hỗ trợ trong React 19 và sẽ tiếp tục chạy, nhưng tài liệu React khuyên dùng function component với hook cho code mới. Bạn vẫn cần một class để viết error boundary.

Class component và function component khác nhau thế nào?

Class component giữ state trong this.state, cập nhật nó bằng this.setState và phản ứng với vòng đời qua các phương thức có tên cố định. Function component dùng hook (useState, useEffect) cho cùng những việc đó, không cần this và ngắn gọn hơn.

setState có cập nhật state ngay lập tức không?

Không. this.setState lên lịch một lần cập nhật và React render sau đó, nên ngay sau lệnh gọi, this.state vẫn giữ giá trị cũ. Hãy truyền vào một hàm, this.setState((state) => ...), khi giá trị mới phụ thuộc vào giá trị cũ, hoặc đọc kết quả trong callback ở đối số thứ hai hay trong componentDidUpdate.

Vì sao this là undefined trong trình xử lý sự kiện của tôi?

Một phương thức của class được truyền dưới dạng onClick={this.handleClick} sẽ được gọi mà không kèm đối tượng của nó, nên this là undefined. Hãy định nghĩa handler là một arrow function trong class field (handleClick = () => { ... }) hoặc bind nó trong constructor.

PureComponent là gì?

Là một Component bỏ qua việc render khi props và state của nó bằng nông (shallow) với lần trước. Đây là phiên bản class của React.memo.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu