ReactJS: Cách xử lý khi tải dữ liệu không đồng bộ (async), tải chậm (lazy loading) và phân tách mã bằng React


Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên

Giới thiệu

Là một nhà phát triển web JavaScript, mã không đồng bộ (asynchronous code) cung cấp cho bạn khả năng chạy một số phần mã của bạn trong khi các phần khác vẫn đang chờ dữ liệu hoặc giải quyết. Điều này có nghĩa là các phần quan trọng của ứng dụng của bạn sẽ không phải đợi các phần ít quan trọng hơn trước khi chúng hiển thị. Với mã không đồng bộ, bạn cũng có thể cập nhật ứng dụng của mình bằng cách yêu cầu và hiển thị thông tin mới, mang đến cho người dùng trải nghiệm mượt mà ngay cả khi các chức năng và yêu cầu dài đang xử lý ở chế độ nền.

Trong phát triển React, lập trình không đồng bộ đưa ra các vấn đề duy nhất. Ví dụ, khi bạn sử dụng các component dạng hàm React, các hàm không đồng bộ có thể tạo ra các vòng lặp vô hạn. Khi một component tải, nó có thể bắt đầu một hàm không đồng bộ và khi hàm không đồng bộ được giải quyết, nó có thể kích hoạt một kết xuất lại khiến component đó gọi lại hàm không đồng bộ. Hướng dẫn này sẽ giải thích cách tránh điều này với một Hook đặc biệt gọi là useEffect, sẽ chỉ chạy các hàm khi dữ liệu cụ thể thay đổi. Điều này sẽ cho phép bạn chạy mã không đồng bộ của mình một cách có chủ ý thay vì trên mỗi chu kỳ hiển thị.

Mã không đồng bộ không chỉ giới hạn ở các yêu cầu đối với dữ liệu mới. React đã tích hợp sẵn trong hệ thống các component tải chậm (lazy loading), hoặc tải chúng chỉ khi người sử dụng cần chúng. Khi kết hợp với cấu hình webpack mặc định trong Create React App, bạn có thể chia nhỏ mã của mình, có thể tách một ứng dụng lớn thành các phần nhỏ hơn có thể được tải khi cần thiết. React có một component đặc biệt được gọi là Suspense, nó sẽ hiển thị các trình giữ chỗ trong khi trình duyệt đang tải component mới của bạn. Trong các phiên bản React trong tương lai, bạn sẽ có thể sử dụng Suspense để tải dữ liệu trong các component lồng nhau mà không bị chặn hiển thị.

Trong bài hướng dẫn này, bạn sẽ xử lý dữ liệu không đồng bộ trong React bằng cách tạo một ứng dụng hiển thị thông tin về các dòng sông (River) và mô phỏng các yêu cầu tới API Web với setTimeout. Đến cuối hướng dẫn này, bạn sẽ có thể tải dữ liệu không đồng bộ bằng Hook useEffect. Bạn cũng sẽ có thể cập nhật trang một cách an toàn mà không tạo ra lỗi nếu component ngắt kết nối trước khi phân giải dữ liệu. Cuối cùng, bạn sẽ chia một ứng dụng lớn thành các phần nhỏ hơn bằng cách sử dụng phương pháp tách mã.

Bước 1 - Tạo một dự án trống

Trong bước này, bạn sẽ tạo một dự án mới bằng Create React App. Sau đó, bạn sẽ xóa dự án mẫu và các tệp liên quan được cài đặt khi bạn khởi động dự án. Cuối cùng, bạn sẽ tạo một cấu trúc tệp đơn giản để tổ chức các component của mình. Điều này sẽ cung cấp cho bạn một cơ sở vững chắc để xây dựng ứng dụng mẫu của hướng dẫn này để tạo style trong bước tiếp theo.

Để bắt đầu, hãy thực hiện một dự án mới. Bạn mở terminal và chạy tập lệnh sau để cài đặt một dự án mới bằng cách sử dụng create-react-app:

npx create-react-app lazy-tutorial

Sau khi dự án kết thúc, hãy thay đổi vào thư mục:

cd lazy-tutorial

Khởi động dự án với lệnh:

npm start
Bạn sẽ nhận được một máy chủ cục bộ đang chạy. Nếu dự án không mở trong cửa sổ trình duyệt, bạn có thể mở nó bằng http://localhost:3000/. Nếu bạn đang chạy điều này từ một máy chủ từ xa, địa chỉ sẽ là .http://your_domain:3000

Trình duyệt của bạn sẽ tải với một ứng dụng React đơn giản được bao gồm như một phần của Create React App:

Dự án mẫu React

Bạn sẽ xây dựng một tập hợp các component tùy chỉnh hoàn toàn mới, vì vậy bạn sẽ cần bắt đầu bằng cách xóa một số mã soạn sẵn để bạn có thể có một dự án trống.

Để bắt đầu, hãy mở component src/App.js. Đây là component gốc được đưa vào trang. Tất cả các component sẽ bắt đầu từ đây. Bạn sửa lại file để trong đó chỉ còn chứa như sau:

import './App.css';

function App() {
  return <></>;
}

export default App;

Mở một terminal khác và thực hiện thao tác xóa file logo.svg:

rm src/logo.svg

Tạo thư mục components:

mkdir src/components

Tạo thư mục App:

mkdir src/components/App

Di chuyển các file App.* vào thư mục App:

mv src/App.* src/components/App

Mở file index.js và chỉnh sửa:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App/App';
import * as serviceWorker from './reportWebVitals';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

Lưu file lại và quay lại trang web ta được một trang web trống.

màn hình trống trong chrome

Bây giờ bạn đã hoàn thành dự án Tạo ứng dụng React mẫu, hãy tạo một cấu trúc tệp đơn giản. Điều này sẽ giúp bạn giữ cho các component của bạn cô lập và độc lập.

Tạo một thư mục được gọi components trong thư mục src, components sẽ là nơi chứa tất cả các component tùy chỉnh của bạn.

Bước 2 - Tải dữ liệu không đồng bộ với useEffect

Trong bước này, bạn sẽ sử dụng Hook useEffect để tải dữ liệu không đồng bộ vào một ứng dụng mẫu. Bạn sẽ sử dụng Hook để ngăn tìm nạp dữ liệu không cần thiết, thêm trình giữ chỗ trong khi dữ liệu đang tải và cập nhật component khi dữ liệu phân giải. Đến cuối bước này, bạn sẽ có thể tải dữ liệu useEffect và thiết lập dữ liệu bằng Hook useState khi nó được giải quyết.

Để khám phá chủ đề này, bạn sẽ tạo một ứng dụng để hiển thị thông tin về những con sông dài nhất trên thế giới. Bạn sẽ tải dữ liệu bằng một hàm không đồng bộ mô phỏng một yêu cầu đến nguồn dữ liệu bên ngoài.

Đầu tiên, tạo một component tên RiverInformation. Tạo thư mục:

mkdir src/services

Thư mục này sẽ chứa các hàm không đồng bộ của bạn. Tạo và mở file rivers.js. Bên trong file ta export một hàm tên getRiverInformation, hàm này trả về một promise. Bên trong promise, hãy thêm một  hàm setTimeout sẽ thực hiện promise sau 1500 mili giây. Điều này sẽ cung cấp cho bạn một chút thời gian để xem component sẽ hiển thị như thế nào trong khi chờ dữ liệu được giải quyết:

export function getRiverInformation() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({
        continent: 'Africa',
        length: '6,650 km',
        outflow: 'Mediterranean'
      })
    }, 1500)
  })
}

Trong đoạn code trên, bạn đã mã hóa thông tin sông, nhưng hàm này sẽ tương tự với bất kỳ hàm không đồng bộ nào mà bạn có thể sử dụng, chẳng hạn như lệnh gọi API. Phần quan trọng là trả về một promise.

Lưu file lại.

Bây giờ bạn có một dịch vụ trả về dữ liệu, bạn cần thêm nó vào component của mình. Điều này đôi khi có thể dẫn đến một vấn đề. Giả sử bạn đã gọi hàm không đồng bộ bên trong component của mình và sau đó đặt dữ liệu thành một biến bằng cách sử dụng Hook useState. Mã sẽ như thế này:

import { useState } from 'react';
import { getRiverInformation } from '../../services/rivers';

export default function RiverInformation() {
  const [riverInformation, setRiverInformation] = useState({});

  getRiverInformation()
  .then(d => {
    setRiverInformation(d)
  })

  return(
    ...
  )
}

Khi bạn thiết lập dữ liệu, thay đổi Hook sẽ kích hoạt kết xuất lại các component. Khi component hiển thị lại, hàm getRiverInformation sẽ chạy lại và khi nó được giải quyết, nó sẽ thiết lập state, điều này sẽ kích hoạt một kết xuất lại khác. Vòng lặp sẽ tiếp tục mãi mãi.

Để giải quyết vấn đề này, React có một Hook đặc biệt được gọi là useEffect, nó sẽ chỉ chạy khi dữ liệu cụ thể thay đổi.

Hook useEffect chấp nhận một hàm như là đối số đầu tiên và một mảng các trigger như là đối số thứ hai. Hàm sẽ chạy trong lần hiển thị đầu tiên sau khi thực hiện bố cục và hiển thị nội dung ra trình duyệt. Sau đó, nó sẽ chỉ chạy nếu một trong các trình kích hoạt thay đổi. Nếu bạn cung cấp một mảng trống, nó sẽ chỉ chạy một lần. Nếu bạn không bao gồm một loạt các trình kích hoạt, nó sẽ chạy sau mỗi lần hiển thị.

Mở RiverInformation.js ra, sử dụng Hook useState để tạo một biến tên riverInformation và một hàm tên setRiverInformation. Bạn sẽ cập nhật component bằng cách đặt thời điểm riverInformation giải quyết hàm không đồng bộ. Sau đó đưa hàm getRiverInformation vào useEffect. Cần lưu ý đảm bảo truyền một mảng trống làm đối số thứ hai. Khi promise được giải quyết, hãy cập nhật giá trị cho biến riverInformation bằng hàm setRiverInformation:

import { useEffect, useState } from 'react';
import { getRiverInformation } from '../../services/rivers';

export default function RiverInformation() {
  const [riverInformation, setRiverInformation] = useState({});

  useEffect(() => {
   getRiverInformation()
   .then(data =>
     setRiverInformation(data)
   );
  }, [])


  return(
    <div>
      <h2>River Information</h2>
      <ul>
        <li>Continent: {riverInformation.continent}</li>
        <li>Length: {riverInformation.length}</li>
        <li>Outflow: {riverInformation.outflow}</li>
      </ul>
    </div>
  )
}

Sau khi hàm không đồng bộ được giải quyết, hãy cập nhật danh sách không có thứ tự với thông tin mới.

Lưu file lại và quay lại trang web ta được:

Cập nhật thông tin sông sau khi tải, 2

Lưu ý rằng component thị trước khi dữ liệu được tải. Ưu điểm của mã không đồng bộ là nó sẽ không chặn kết xuất ban đầu. Trong trường hợp này, bạn có một component hiển thị danh sách mà không có bất kỳ dữ liệu nào, nhưng bạn cũng có thể hiển thị một spinner hoặc một trình giữ chỗ đồ họa vectơ có thể mở rộng (Scalable Viector Graphic - SVG).

Đôi khi bạn chỉ cần tải dữ liệu một lần, chẳng hạn như nếu bạn đang lấy thông tin người dùng hoặc danh sách tài nguyên không bao giờ thay đổi. Nhưng nhiều khi hàm không đồng bộ của bạn sẽ yêu cầu một số đối số. Trong những trường hợp đó, bạn sẽ cần phải kích hoạt sử dụng Hook useEffect bất cứ khi nào dữ liệu thay đổi.

Để mô phỏng điều này, hãy thêm một số dữ liệu khác vào dịch vụ của bạn. Mở rivers.js ra và update code như sau:

const rivers = {
 nile: {
   continent: 'Africa',
   length: '6,650 km',
   outflow: 'Mediterranean'
 },
 amazon: {
   continent: 'South America',
   length: '6,575 km',
   outflow: 'Atlantic Ocean'
 },
 yangtze: {
   continent: 'Asia',
   length: '6,300 km',
   outflow: 'East China Sea'
 },
 mississippi: {
   continent: 'North America',
   length: '6,275 km',
   outflow: 'Gulf of Mexico'
 }
}

export function getRiverInformation(name) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(
        rivers[name]
      )
    }, 1500)
  })
}

Lưu file lại. Tiếp theo, mở App.js để bạn có thể thêm các tùy chọn khác:

import { useState } from 'react';
import './App.css';
import RiverInformation from '../RiverInformation/RiverInformation';

function App() {
  const [river, setRiver] = useState('nile');
  return (
    <div className="wrapper">
      <h1>World's Longest Rivers</h1>
      <button onClick={() => setRiver('nile')}>Nile</button>
      <button onClick={() => setRiver('amazon')}>Amazon</button>
      <button onClick={() => setRiver('yangtze')}>Yangtze</button>
      <button onClick={() => setRiver('mississippi')}>Mississippi</button>
      <RiverInformation name={river} />
    </div>
  );
}

export default App;

Lưu file lại. Tiếp theo, mở RiverInformation.js ra, kéo vào name như một prop và truyền nó vào hàm getRiverInformation. Hãy chắc chắn thêm name vào mảng cho useEffect, nếu không nó sẽ không chạy lại:

import { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { getRiverInformation } from '../../services/rivers';

export default function RiverInformation({ name }) {
  const [riverInformation, setRiverInformation] = useState({});

  useEffect(() => {
    getRiverInformation(name)
    .then(data =>
      setRiverInformation(data)
    );
  }, [name])


  return(
    <div>
      <h2>River Information</h2>
      <ul>
        <li>Continent: {riverInformation.continent}</li>
        <li>Length: {riverInformation.length}</li>
        <li>Outflow: {riverInformation.outflow}</li>
      </ul>
    </div>
  )
}

RiverInformation.propTypes = {
 name: PropTypes.string.isRequired
}

Trong đoạn code trên, bạn cũng đã thêm một PropTypes, hệ thống này sẽ đảm bảo rằng cột chống là một chuỗi.

Lưu file lại và quay lại trang web, và bạn có thể chọn các sông khác nhau. Lưu ý độ trễ giữa những lần bạn nhấp và khi dữ liệu hiển thị:

Cập nhật thông tin sông, 3

Nếu bạn bỏ sót phần prop name khỏi mảng trong useEffect, bạn sẽ nhận được cảnh báo xây dựng trong console của trình duyệt. Nó sẽ có dạng như thế này:

Error
src\components\RiverInformation\RiverInformation.js
Line 13:6:  React Hook useEffect has a missing dependency: 'name'. Either include it or remove the dependency array  react-hooks/exhaustive-deps

Lỗi này cho bạn biết rằng hàm trong hiệu ứng của bạn có các phụ thuộc mà bạn không thiết lập rõ ràng. Trong tình huống này, rõ ràng là hiệu ứng sẽ không hoạt động, nhưng đôi khi bạn có thể so sánh dữ liệu hỗ trợ với dữ liệu state bên trong component, điều này khiến bạn có thể mất dấu các mục trong mảng.

Điều cuối cùng cần làm là thêm một số lập trình phòng thủ vào component của bạn. Đây là một nguyên tắc thiết kế nhấn mạnh tính khả dụng cao cho ứng dụng của bạn. Bạn muốn đảm bảo rằng component của bạn sẽ hiển thị ngay cả khi dữ liệu không ở đúng kiểu hoặc nếu bạn không nhận được bất kỳ dữ liệu nào từ một yêu cầu API.

Như ứng dụng của bạn hiện tại, hiệu ứng sẽ cập nhật riverInformation bất kỳ loại dữ liệu nào mà ứng dụng nhận được. Đây thường sẽ là một đối tượng, nhưng trong trường hợp không phải, bạn có thể sử dụng chuỗi tùy chọn để đảm bảo rằng bạn sẽ không gặp lỗi.

Bên trong RiverInformation.js, thay thế thể hiện của một đối tượng dạng (.) bằng dạng (?.). Để kiểm tra xem nó có hoạt động hay không, hãy xóa đối tượng mặc định {} khỏi hàm useState:

import { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { getRiverInformation } from '../../services/rivers';

export default function RiverInformation({ name }) {
  const [riverInformation, setRiverInformation] = useState();

  useEffect(() => {
    getRiverInformation(name)
    .then(data =>
      setRiverInformation(data)
    );
  }, [name])

  return(
    <div>
      <h2>River Information</h2>
      <ul>
        <li>Continent: {riverInformation?.continent}</li>
        <li>Length: {riverInformation?.length}</li>
        <li>Outflow: {riverInformation?.outflow}</li>
      </ul>
    </div>
  )
}

RiverInformation.propTypes = {
  name: PropTypes.string.isRequired
}

Lưu file lại và quay lại trang web, bạn sẽ thấy trang vẫn hoạt động mặc dù mã đang tham chiếu đến thuộc tính undefined thay vì một đối tượng:

Lập trình phòng thủ thường được coi là phương pháp hay nhất, nhưng nó đặc biệt quan trọng đối với các hàm không đồng bộ như lệnh gọi API khi bạn không thể đảm bảo phản hồi.

Trong bước này, bạn đã gọi các hàm không đồng bộ trong React. Bạn đã sử dụng Hook useEffect để tìm nạp thông tin mà không kích hoạt hiển thị lại và kích hoạt cập nhật mới bằng cách thêm điều kiện vào mảng trong useEffect.

Trong bước tiếp theo, bạn sẽ thực hiện một số thay đổi đối với ứng dụng của mình để ứng dụng chỉ cập nhật các component khi chúng được gắn kết. Điều này sẽ giúp ứng dụng của bạn tránh bị rò rỉ bộ nhớ.

Bước 3 - Ngăn ngừa lỗi trên các component chưa được gắn kết

Trong bước này, bạn sẽ ngăn cập nhật dữ liệu trên các component chưa được gắn kết. Vì bạn không bao giờ có thể chắc chắn khi nào dữ liệu sẽ giải quyết bằng lập trình không đồng bộ, nên luôn có nguy cơ dữ liệu sẽ giải quyết sau khi component đã bị xóa. Việc cập nhật dữ liệu trên một component chưa được gắn kết là không hiệu quả và có thể gây rò rỉ bộ nhớ (memory leak) trong đó ứng dụng của bạn đang sử dụng nhiều bộ nhớ hơn mức cần thiết.

Đến cuối bước này, bạn sẽ biết cách ngăn chặn rò rỉ bộ nhớ bằng cách thêm các bộ bảo vệ trong Hook useEffect của bạn để chỉ cập nhật dữ liệu khi component được gắn kết.

Component hiện tại sẽ luôn được gắn kết, vì vậy không có khả năng mã sẽ thử và cập nhật component sau khi nó bị xóa khỏi DOM, nhưng hầu hết các component không đáng tin cậy như vậy. Chúng sẽ được thêm và xóa khỏi trang khi người dùng tương tác với ứng dụng. Nếu một component bị xóa khỏi trang trước khi hàm không đồng bộ giải quyết, bạn có thể bị rò rỉ bộ nhớ.

Để kiểm tra vấn đề, hãy cập nhật App.js để có thể thêm và xóa các chi tiết về dòng sông.

Mở App.js ra, thêm một nút để chuyển đổi chi tiết sông. Sử dụng Hook useReducer để tạo một hàm để chuyển đổi các chi tiết và một biến để lưu trữ state được bật tắt:

import { useReducer, useState } from 'react';
import './App.css';
import RiverInformation from '../RiverInformation/RiverInformation';

function App() {
  const [river, setRiver] = useState('nile');
  const [show, toggle] = useReducer(state => !state, true);
  return (
    <div className="wrapper">
      <h1>World's Longest Rivers</h1>
      <div><button onClick={toggle}>Toggle Details</button></div>
      <button onClick={() => setRiver('nile')}>Nile</button>
      <button onClick={() => setRiver('amazon')}>Amazon</button>
      <button onClick={() => setRiver('yangtze')}>Yangtze</button>
      <button onClick={() => setRiver('mississippi')}>Mississippi</button>
      {show && <RiverInformation name={river} />}
    </div>
  );
}

export default App;

Lưu file lại và quay lại trang web, bạn có thể chuyển đổi các chi tiết.

Nhấp vào một con sông, sau đó nhấp ngay vào nút Toggle Details để ẩn thông tin chi tiết. React sẽ tạo ra một cảnh báo lỗi rằng có khả năng bị rò rỉ bộ nhớ.

Cảnh báo khi thành phần được cập nhật sau khi bị xóa, 5

Để khắc phục sự cố, bạn cần phải hủy hoặc bỏ qua chức năng không đồng bộ bên trong useEffect. Nếu bạn đang sử dụng một thư viện như RxJS , bạn có thể hủy một hành động không đồng bộ khi thành phần ngắt kết nối bằng cách trả về một hàm trong useEffectHook của bạn . Trong các trường hợp khác, bạn sẽ cần một biến để lưu trữ trạng thái được gắn kết.

Mở RiverInformation.js ra, bên trong hàm useEffect, hãy tạo một biến được gọi là mounted và đặt nó thành true. Bên trong callback .then, ta sử dụng một điều kiện để đặt dữ liệu nếu mounted là true:

import { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { getRiverInformation } from '../../services/rivers';

export default function RiverInformation({ name }) {
  const [riverInformation, setRiverInformation] = useState();

  useEffect(() => {
    let mounted = true;
    getRiverInformation(name)
    .then(data => {
      if(mounted) {
        setRiverInformation(data)
      }
    });
  }, [name])


  return(
    <div>
      <h2>River Information</h2>
      <ul>
        <li>Continent: {riverInformation?.continent}</li>
        <li>Length: {riverInformation?.length}</li>
        <li>Outflow: {riverInformation?.outflow}</li>
      </ul>
    </div>
  )
}

RiverInformation.propTypes = {
  name: PropTypes.string.isRequired
}

Bây giờ bạn đã có biến mounted, bạn cần có thể lật giá trị của nó khi component ngắt kết nối. Với Hook useEffect, bạn có thể trả về một hàm sẽ chạy khi component ngắt kết nối. Trả về một hàm đặt mounted thành false:

import { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { getRiverInformation } from '../../services/rivers';

export default function RiverInformation({ name }) {
  const [riverInformation, setRiverInformation] = useState();

  useEffect(() => {
    let mounted = true;
    getRiverInformation(name)
    .then(data => {
      if(mounted) {
        setRiverInformation(data)
      }
    });
    return () => {
      mounted = false;
    }
  }, [name])

  return(
    <div>
      <h2>River Information</h2>
      <ul>
        <li>Continent: {riverInformation?.continent}</li>
        <li>Length: {riverInformation?.length}</li>
        <li>Outflow: {riverInformation?.outflow}</li>
      </ul>
    </div>
  )
}

RiverInformation.propTypes = {
  name: PropTypes.string.isRequired
}

Lưu file lại và quay lại trang web, bạn sẽ có thể chuyển đổi các chi tiết mà không còn gặp lỗi nữa.

Không có cảnh báo khi bật / tắt, 6

Khi bạn ngắt kết nối, component useEffect sẽ cập nhật biến. Hàm không đồng bộ sẽ vẫn giải quyết, nhưng nó sẽ không thực hiện bất kỳ thay đổi nào đối với các component chưa được gắn kết. Điều này sẽ ngăn chặn rò rỉ bộ nhớ.

Trong bước này, bạn chỉ đặt trạng thái cập nhật ứng dụng khi một component được gắn kết. Bạn đã cập nhật Hook useEffect để theo dõi xem component có được gắn kết hay không và trả về một hàm để cập nhật giá trị khi component ngắt kết nối.

Trong bước tiếp theo, bạn sẽ tải các component không đồng bộ để chia mã thành các gói nhỏ hơn mà người dùng sẽ tải khi cần thiết.

Bước 4 - Tải chậm component với Suspense và lazy

Trong bước này, bạn sẽ tách mã của mình bằng React Suspense và lazy. Khi các ứng dụng phát triển, kích thước của bản dựng cuối cùng cũng tăng theo. Thay vì buộc người dùng tải xuống toàn bộ ứng dụng, bạn có thể chia mã thành các phần nhỏ hơn. React Suspense và lazy làm việc với webpack và các hệ thống xây dựng khác để chia mã của bạn thành các phần nhỏ hơn mà người dùng có thể tải theo yêu cầu. Trong tương lai, bạn sẽ có thể sử dụng Suspense để tải nhiều loại dữ liệu, bao gồm cả các yêu cầu API.

Khi kết thúc bước này, bạn sẽ có thể tải các component không đồng bộ, chia các ứng dụng lớn thành các phần nhỏ hơn, tập trung hơn.

Cho đến nay, bạn chỉ làm việc với việc tải dữ liệu không đồng bộ, nhưng bạn cũng có thể tải các component không đồng bộ. Quá trình này, thường được gọi là tách mã, giúp giảm kích thước các gói mã của bạn để người dùng của bạn không phải tải xuống ứng dụng đầy đủ nếu họ chỉ sử dụng một phần của nó.

Hầu hết thời gian, bạn nhập mã tĩnh, nhưng bạn có thể nhập mã động bằng cách gọi import dưới dạng một hàm thay vì một câu lệnh. Mã sẽ như thế này:

import('my-library')
.then(library => library.action())

React cung cấp cho bạn một bộ công cụ bổ sung có tên lazy và Suspense. React Suspense cuối cùng sẽ mở rộng để xử lý tải dữ liệu, nhưng hiện tại bạn có thể sử dụng nó để tải các component.

Mở file App.js ra, sau đó import lazy và Suspense từ react:

import { lazy, Suspense, useReducer, useState } from 'react';
import './App.css';
import RiverInformation from '../RiverInformation/RiverInformation';

function App() {
  const [river, setRiver] = useState('nile');
  const [show, toggle] = useReducer(state => !state, true);
  return (
    <div className="wrapper">
      <h1>World's Longest Rivers</h1>
      <div><button onClick={toggle}>Toggle Details</button></div>
      <button onClick={() => setRiver('nile')}>Nile</button>
      <button onClick={() => setRiver('amazon')}>Amazon</button>
      <button onClick={() => setRiver('yangtze')}>Yangtze</button>
      <button onClick={() => setRiver('mississippi')}>Mississippi</button>
      {show && <RiverInformation name={river} />}
    </div>
  );
}

export default App;

lazy và Suspsense có hai công việc riêng biệt. Bạn sử dụng hàm lazy để import động component và đặt nó thành một biến. Suspense là một component tích hợp mà bạn sử dụng để hiển thị thông báo dự phòng trong khi mã đang tải.

Thay thế import RiverInformation from '../RiverInformation/RiverInformation'; bằng một lời gọi đến lazy. Gán kết quả cho một biến được gọi là RiverInformation. Sau đó, bao ngoài {show && <RiverInformation name={river} />} bởi component Suspense và một <div> mang thông báo Loading Component tới prop fallback:

import React, { lazy, Suspense, useReducer, useState } from 'react';
import './App.css';
const RiverInformation = lazy(() => import('../RiverInformation/RiverInformation'));

function App() {
  const [river, setRiver] = useState('nile');
  const [show, toggle] = useReducer(state => !state, true);
  return (
    <div className="wrapper">
      <h1>World's Longest Rivers</h1>
      <div><button onClick={toggle}>Toggle Details</button></div>
      <button onClick={() => setRiver('nile')}>Nile</button>
      <button onClick={() => setRiver('amazon')}>Amazon</button>
      <button onClick={() => setRiver('yangtze')}>Yangtze</button>
      <button onClick={() => setRiver('mississippi')}>Mississippi</button>
      <Suspense fallback={<div>Loading Component</div>}>
        {show && <RiverInformation name={river} />}
      </Suspense>
    </div>
  );
}

export default App;

Lưu file lại và quay lại trang web, bạn hãy tải lại trang và bạn sẽ thấy rằng component được tải động. Nếu bạn muốn xem thông báo đang tải, bạn có thể điều chỉnh phản hồi trong trình duyệt web Chrome.

Nếu điều hướng đến tab Network trong Chrome hoặc Firefox, bạn sẽ thấy rằng mã được chia thành nhiều phần khác nhau.

Theo mặc định, mỗi chunk sẽ nhận được một số, nhưng với việc Create React App kết hợp với webpack, bạn có thể đặt tên chunk bằng cách thêm comment vào import động.

Trong file App.js, thêm component /* webpackChunkName: "RiverInformation" */ vào bên trong hàm import:

import { lazy, Suspense, useReducer, useState } from 'react';
import './App.css';
const RiverInformation = lazy(() => import(/* webpackChunkName: "RiverInformation" */ '../RiverInformation/RiverInformation'));

function App() {
  const [river, setRiver] = useState('nile');
  const [show, toggle] = useReducer(state => !state, true);
  return (
    <div className="wrapper">
      <h1>World's Longest Rivers</h1>
      <div><button onClick={toggle}>Toggle Details</button></div>
      <button onClick={() => setRiver('nile')}>Nile</button>
      <button onClick={() => setRiver('amazon')}>Amazon</button>
      <button onClick={() => setRiver('yangtze')}>Yangtze</button>
      <button onClick={() => setRiver('mississippi')}>Mississippi</button>
      <Suspense fallback={<div>Loading Component</div>}>
        {show && <RiverInformation name={river} />}
      </Suspense>
    </div>
  );
}

export default App;

Lưu file lại và quay lại trang web, bạn sẽ thấy đoạn mã RiverInformation sẽ có một tên duy nhất.

Chunk thông tin sông

Trong bước này, bạn đã tải không đồng bộ các component. Bạn đã sử dụng lazy và Suspense để import động các component và hiển thị thông báo đang tải trong khi tải component. Bạn cũng đã đặt tên tùy chỉnh cho các khối webpack để cải thiện khả năng đọc và gỡ lỗi.

Phần kết luận

Các hàm không đồng bộ tạo ra các ứng dụng thân thiện với người dùng hiệu quả. Tuy nhiên, lợi thế của chúng đi kèm với một số chi phí nhỏ có thể phát triển thành lỗi trong chương trình của bạn. Giờ đây, bạn có các công cụ cho phép bạn chia các ứng dụng lớn thành các phần nhỏ hơn và tải dữ liệu không đồng bộ trong khi vẫn cung cấp cho người dùng một ứng dụng hiển thị hoàn chỉnh. Bạn có thể sử dụng kiến ​​thức để kết hợp các yêu cầu API và thao tác dữ liệu không đồng bộ vào các ứng dụng của mình, tạo ra trải nghiệm người dùng nhanh chóng và đáng tin cậy hơn.

» Tiếp: Cách gọi API web với Hook useEffect trong React
« Trước: Cách xây dựng biểu mẫu (form) trong React
Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên
Copied !!!