Lifecycle Component trong ReactJS là quá trình một component trải qua từ khi được tạo ra đến khi bị hủy. Nó gồm ba giai đoạn chính: Mounting (gắn kết), Updating (cập nhật), và Unmounting (hủy bỏ). Mỗi giai đoạn có các phương thức đặc trưng như constructor, render, componentDidMount trong Mounting; shouldComponentUpdate, componentDidUpdate trong Updating; và componentWillUnmount trong Unmounting. React cũng cung cấp các phương thức xử lý lỗi như getDerivedStateFromError và componentDidCatch. Hiểu rõ về lifecycle giúp lập trình viên kiểm soát tốt hơn cách component hoạt động, tối ưu hiệu suất và xử lý side effects. Với sự ra đời của Hooks, functional components giờ đây cũng có thể mô phỏng các tính năng lifecycle thông qua useEffect.

1. Mounting (Gắn kết)

constructor(props)

  • Được gọi khi một component được khởi tạo.
  • Dùng để khởi tạo state và binding các phương thức.
class ExampleComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(prevState => ({ count: prevState.count + 1 }));
  }

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.handleClick}>Increment</button>
      </div>
    );
  }
}

static getDerivedStateFromProps(props, state)

  • Được gọi ngay trước khi render, trong cả mounting và updating.
  • Dùng để cập nhật state dựa trên props.
class ExampleComponent extends React.Component {
  state = { localCount: 0 };

  static getDerivedStateFromProps(props, state) {
    if (props.count !== state.localCount) {
      return { localCount: props.count };
    }
    return null;
  }

  render() {
    return <div>Count: {this.state.localCount}</div>;
  }
}

render()

  • Phương thức bắt buộc, trả về JSX để hiển thị.
class ExampleComponent extends React.Component {
  render() {
    return (
      <div>
        <h1>{this.props.title}</h1>
        <p>{this.props.content}</p>
      </div>
    );
  }
}

componentDidMount()

  • Được gọi sau khi component đã được render vào DOM.
  • Thích hợp cho việc khởi tạo network request, DOM manipulation.
class ExampleComponent extends React.Component {
  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  tick() {
    this.setState(prevState => ({ time: new Date() }));
  }

  render() {
    return <div>Current time: {this.state.time.toLocaleTimeString()}</div>;
  }
}

2. Updating (Cập nhật)

shouldComponentUpdate(nextProps, nextState)

  • Quyết định xem component có nên re-render hay không.
  • Dùng để tối ưu hiệu suất.
class ExampleComponent extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    return this.props.value !== nextProps.value;
  }

  render() {
    return <div>{this.props.value}</div>;
  }
}

getSnapshotBeforeUpdate(prevProps, prevState)

  • Được gọi ngay trước khi thay đổi từ render được commit vào DOM.
  • Dùng để lấy thông tin từ DOM trước khi nó có thể thay đổi.
class ScrollingList extends React.Component {
  getSnapshotBeforeUpdate(prevProps, prevState) {
    if (prevProps.list.length < this.props.list.length) {
      const list = this.listRef.current;
      return list.scrollHeight - list.scrollTop;
    }
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    if (snapshot !== null) {
      const list = this.listRef.current;
      list.scrollTop = list.scrollHeight - snapshot;
    }
  }

  render() {
    return (
      <div ref={this.listRef}>
        {this.props.list.map(item => <div key={item.id}>{item.content}</div>)}
      </div>
    );
  }
}

componentDidUpdate(prevProps, prevState, snapshot)

  • Được gọi sau khi cập nhật xảy ra.
  • Thích hợp cho việc thực hiện side effects.
class ExampleComponent extends React.Component {
  componentDidUpdate(prevProps) {
    if (this.props.userID !== prevProps.userID) {
      this.fetchData(this.props.userID);
    }
  }

  fetchData(userID) {
    // Giả sử hàm này gọi API để lấy dữ liệu người dùng
  }

  render() {
    return <div>User data for ID: {this.props.userID}</div>;
  }
}

3. Unmounting (Hủy bỏ)

componentWillUnmount()

  • Được gọi ngay trước khi component bị hủy và loại bỏ khỏi DOM.
  • Dùng để dọn dẹp (cleanup) như hủy timers, cancel network requests.
class ExampleComponent extends React.Component {
  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState(prevState => ({ time: new Date() }));
  }

  render() {
    return <div>Current time: {this.state.time.toLocaleTimeString()}</div>;
  }
}

4. Error Handling (Xử lý lỗi)

static getDerivedStateFromError(error)

  • Được gọi khi một component con ném ra lỗi.
  • Dùng để cập nhật state để hiển thị UI lỗi thay thế.
class ErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

componentDidCatch(error, info)

  • Được gọi sau getDerivedStateFromError.
  • Dùng để ghi log lỗi.
class ErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    logErrorToMyService(error, info);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

Hooks và Functional Components

Từ React 16.8+, Hooks đã được giới thiệu, cho phép sử dụng state và các tính năng React khác mà không cần viết class. Dưới đây là ví dụ về cách sử dụng useEffect hook để mô phỏng các lifecycle methods trong functional components:

import React, { useState, useEffect } from 'react';

function ExampleComponent() {
  const [count, setCount] = useState(0);

  // Tương đương với componentDidMount và componentDidUpdate
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  // Tương đương với componentDidMount
  useEffect(() => {
    const timer = setInterval(() => {
      console.log('Timer running');
    }, 1000);

    // Tương đương với componentWillUnmount
    return () => {
      clearInterval(timer);
    };
  }, []); // Empty array means this effect runs once on mount and cleanup on unmount

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

Trong ví dụ này, useEffect được sử dụng để mô phỏng các phương thức lifecycle khác nhau. Hook đầu tiên chạy sau mỗi lần render, trong khi hook thứ hai chỉ chạy một lần khi component mount và cleanup khi unmount.

Kết Luận

Lifecycle Component trong ReactJS đóng vai trò quan trọng trong việc quản lý và kiểm soát hành vi của các component trong suốt quá trình tồn tại của chúng. Từ giai đoạn Mounting, qua Updating, đến Unmounting, mỗi giai đoạn cung cấp các phương thức đặc thù cho phép developers can thiệp vào quá trình rendering, cập nhật state và props, thực hiện side effects, và dọn dẹp tài nguyên.

Hiểu rõ về lifecycle giúp lập trình viên tối ưu hiệu suất ứng dụng, xử lý dữ liệu một cách hiệu quả, và tạo ra các component linh hoạt, có khả năng tái sử dụng cao. Đặc biệt, việc nắm vững các phương thức như componentDidMount, shouldComponentUpdate, và componentWillUnmount là chìa khóa để xây dựng các ứng dụng React phức tạp và có hiệu suất cao.

Tuy nhiên, với sự ra đời của React Hooks, cách tiếp cận lifecycle đã có những thay đổi đáng kể. Hooks như useEffect đã mang lại một cách mới để quản lý lifecycle trong functional components, đơn giản hóa code và giảm sự phức tạp. Điều này không có nghĩa là lifecycle trong class components trở nên lỗi thời, mà nó mở ra thêm nhiều lựa chọn cho developers trong việc thiết kế và implement các component.

Trong tương lai, khi React tiếp tục phát triển, chúng ta có thể kỳ vọng vào những cải tiến và tối ưu hóa hơn nữa trong cách quản lý lifecycle. Việc theo dõi và cập nhật kiến thức về những thay đổi này sẽ giúp developers luôn đi đầu trong việc xây dựng các ứng dụng React hiện đại, hiệu quả và dễ bảo trì.

Cuối cùng, dù là sử dụng class components với các phương thức lifecycle truyền thống hay functional components với Hooks, việc hiểu rõ và áp dụng đúng các nguyên tắc của lifecycle sẽ luôn là một kỹ năng quan trọng đối với mọi React developer.