Category: Web

  • Bài toán tìm kiếm tương tự

    Bài toán tìm kiếm tương tự

    Danh mục nội dung

    Tìm kiếm KNN

    Thuật toán K-hàng xóm gần nhất, còn được gọi là KNN hoặc k-NN, là một thuật toán học máy có giám sát, không có tham số, được dùng trong phân loại dữ liệu. Thuật toán này sử dụng khoảng cách để phân loại hoặc dự đoán về một nhóm điểm dữ liệu, dựa trên giả định rằng các điểm dữ liệu tương tự sẽ gần nhau về khoảng cách.

    Ngày nay, tìm kiếm hàng xóm gần nhất đã trở thành một chủ đề nghiên cứu nóng bởi tính ứng dụng thực tiễn của nó, nhằm giúp người dùng có thể dễ dàng tìm thấy thông tin họ đang tìm kiếm trong thời gian hợp lý. Thuật toán k-NN là một trong những kỹ thuật giúp tìm chính xác các hàng xóm gần nhất, bởi nó so sánh khoảng cách của mỗi điểm dữ liệu với mọi điểm dữ liệu khác, vì vậy nó yêu cầu thời gian truy vấn tuyến tính (kích thước tập dữ liệu). Nhưng thật không may, hầu hết các ứng dụng hiện đại ngày nay đều có tập dữ liệu khổng lồ (hàng triệu) với chiều cao (hàng trăm hoặc hàng nghìn), vì vậy mà tìm kiếm tuyến tính sẽ tốn kém thời gian. Hãy thử tưởng tượng một thị trường C2C trong thế giới thực với hàng triệu sản phẩm có trong cơ sở dữ liệu và có thể có hàng nghìn sản phẩm mới được tải lên mỗi ngày. So sánh từng sản phẩm với tất cả hàng triệu sản phẩm là lãng phí và mất quá nhiều thời gian, có thể nói giải pháp này là không thể mở rộng. Ngoài ra, các ứng dụng hiện đại còn có nhiều ràng buộc bổ sung khác như mức tiêu thụ bộ nhớ hợp lý và/hoặc độ trễ thấp.

    Điều quan trọng cần lưu ý là mặc dù đã có rất nhiều tiến bộ gần đây về chủ đề này, nhưng k-NN vẫn là phương pháp khả dụng duy nhất để đảm bảo truy xuất chính xác hàng xóm gần nhất.

    Tìm kiếm ANN

    Để giải quyết những vấn đề của k-NN, một lớp các thuật toán mới ra đời có tên là ANN (Approximate Nearest Neighbors). Các thuật toán ANN đánh đổi độ chính xác để mang lại hiệu quả tìm kiếm nhanh chóng, trong thời gian chấp nhận được. Trong một thị trường C2C thực tế, nơi mà số lượng hàng xóm thực tế cao hơn K hàng xóm gần nhất cần tìm kiếm rất nhiều, ANN có thể cho phép đạt được độ chính xác đáng kể khi so sánh với KNN, trong một thời gian ngắn.

    Trong các ứng dụng hiện đại, sai số nhỏ về độ chính xác đổi lại với độ trễ thấp mang lại nhiều lợi ích cho người dùng. Hai ví dụ dưới đây cho thấy điều đó:

    • Tìm kiếm trực quan – Là một người dùng, nếu tôi đang muốn tìm kiếm một bức ảnh về chiếc giày yêu thích, tôi sẽ không bận tâm đến thứ tự xuất hiện của các kết quả trả về, tôi có thể thỏa mãn nhu cầu tìm kiếm của mình nếu như một số ít kết quả mong muốn được hiển thị gần nhất trong khung nhìn của mình.
    • Các hệ gợi ý – Tương tự như trên, tôi cũng không bận tâm quá nhiều đến thứ tự ưu tiên của các kết quả gần nhất khi mà tôi chỉ cần khoảng 8 đến 10 kết quả tương tự hiển thị trong khung nhìn của mình.

    Các kỹ thuật ANN tăng tốc độ tìm kiếm bằng cách tiền xử lý dữ liệu thành một chỉ mục hiệu quả và thường được xử lý qua các giai đoạn sau:

    • Vector Transformation – được áp dụng trên các véc-tơ trước khi chúng được lập chỉ mục, ví dụ như giảm chiều dữ liệu.
    • Vector Encoding – được áp dụng trên các véc-tơ để xây dựng chỉ mục thực sự cho tìm kiếm; một số kỹ thuật dựa trên cấu trúc dữ liệu được áp dụng như: Cây, LSH, và lượng tử hóa – một kỹ thuật để mã hóa véc-tơ thành dạng nén, nhỏ gọn hơn nhiều.
    • Thành phần loại bỏ tìm kiếm toàn bộ – được áp dụng trên các véc-tơ để tránh tìm kiếm toàn bộ như k-NN diễn ra, sử dụng các kỹ thuật như: Các tệp đảo ngược, các đồ thị hàng xóm lân cận,…

    Vì sự hữu ích cũng như ứng dụng thực tiễn mà ANN mang lại, nên hiện nay đã có một số thuật toán ANN được triển khai nguồn mở và được sử dụng phổ biến, như: Annoy của Spotify [1], ScaNN của Google [2], Faiss của Facebook [3], và Hnsw [4].

    Tuy nhiên, các kỹ thuật ANN cũng tồn tại nhược điểm, một trong số đó là tài nguyên điện toán, cụ thể là RAM, các kỹ thuật này phải tải toàn bộ các véc-tơ vào RAM để có thể truy xuất các hàng xóm gần nhất.

    Tài liệu tham khảo

    [1] ANNOY library, https://github.com/spotify/annoy

    [2] ScaNN library, https://github.com/google-research/google-research/tree/master/scann

    [3] Faiss library, https://github.com/facebookresearch/faiss

    [4] Hnsw library, https://github.com/nmslib/hnswlib

    Author

    Ha Huu Linh

  • Một số nguyên lý của Elasticsearch

    Một số nguyên lý của Elasticsearch

    Elasticsearch là một công cụ phân tích và tìm kiếm phân tán theo thời gian thực. Nó cho phép khám phá dữ liệu với tốc độ và quy mô chưa từng có trước đây. Nó được sử dụng trong tìm kiếm toàn văn bản (full-text search), tìm kiếm có cấu trúc, phân tích và kết hợp cả ba. Một số hệ thống nổi tiếng sử dụng Elasticsearch có thể kể đến như: GitHub, StackOverflow, Wikipedia,… Elasticsearch còn là một công cụ tìm kiếm nguồn mở, được xây dựng dựa trên Apache Lucene – một thư viện tìm kiếm toàn văn bản.

    Elasticsearch lưu trữ các documents theo mô hình phân tán, các documents là các đối tượng lưu trữ dữ liệu dưới dạng khóa-giá trị (key-value), có thể được chuyển đổi từ định dạng JSON, và thực tế là Elasticsearch nhận các JSON documents để làm đầu vào cho xử lý hoặc để trả về các kết quả cho máy khách. Elasticsearch không chỉ lưu trữ các documents, nó còn lập chỉ mục (indexing) chúng để làm cho chúng có thể tìm kiếm được.

    Elasticsearch cung cấp khả năng mở rộng và tính khả dụng cao nhờ bản chất phân tán, nhờ che giấu toàn bộ việc quản lý hạ tầng giúp cho ứng dụng có thể dễ dàng làm việc với Elasticsearch mà không cần phải có một hiểu biết sâu sắc về vận hành hạ tầng cũng như tổ chức dữ liệu trong Elasticsearch. Tuy nhiên, để có vể vận hành Elasticsearch hiệu quả, việc hiểu cách Elasticsearch tổ chức dữ liệu là cần thiết. Về mặt vật lý, Elasticsearch tổ chức dữ liệu theo 3 cấp độ, với các khái niệm: cluster, node, và shard.

    Một node là một instance đang chạy của Elasticsearch; trong khi đó một cluster bao gồm 1 hoặc nhiều nodes phối hợp để chia sẻ dữ liệu và workload. Khi một node được thêm vào hoặc loại bỏ khỏi cluster thì cluster sẽ tự tổ chức lại để có thể điều phối dữ liệu đều giữa các nodes. Để đạt được điều đó thì một node trong cluster sẽ được chọn làm master node – đóng vai trò như người quản lý, nó chịu trách nhiệm cho việc quản lý các thay đổi trong cluster như thêm hoặc xóa một node khỏi cluster. Master node không tham gia vào các thay đổi hoặc tìm kiếm ở cấp độ document trừ phi cluster chỉ có 1 node duy nhất, việc này giúp cho nó không trở thành bottleneck của hệ thống khi lưu lượng truy cập tăng lên. Trong Elasticsearch thì bất kỳ node nào cũng có thể trở thành master node. Trong khi nói về tổ chức vật lý bên trong Elasticsearch thì không thể không nói về các shards; trong Elasticsearch thì shard là đơn vị vật lý cấp thấp được sử dụng để tổ chức dữ liệu – nơi mà các documents trong một index sẽ được phân bổ trong một vài shards.

    Hình trên minh họa mối quan hệ giữa index và các shards trong Elasticsearch. Về bản chất, index chỉ là một không gian tên logic chứa các documents, các nhà phát triển phần mềm ứng dụng sẽ làm việc với index thay vì trực tiếp với các shards; trong khi đó ở phía dưới, Elasticsearch sẽ tổ chức các documents trong cùng một index vào các shards.

    Như vậy, khi làm việc với Elasticsearch, chúng ta quan tâm chính là về index. Thuật ngữ index có thể được hiểu theo nhiều nghĩa khác nhau, phụ thuộc vào ngữ cảnh: (i) index là một danh từ chỉ nơi lưu trữ các documents, nó giống như một cơ sở dữ liệu trong các cơ sở dữ liệu truyền thống; (ii index cũng có thể được hiểu là một động từ chỉ việc lưu trữ 1 document vào một index (danh từ), thường được gọi là quá trình indexing (lập chỉ mục); và (iii) inverted index chỉ một cấu trúc dữ liệu mà Elasticsearch và Lucene sử dụng để hỗ trợ truy xuất dữ liệu toàn văn bản nhanh chóng.

    Ngày nay, Elasticsearch đã được sử dụng trong rất nhiều ứng dụng nói chung và thương mại điện tử nói riêng bởi nó được thiết kế để hỗ trợ mạnh mẽ trong tìm kiếm và phân tích với dữ liệu. Elasticsearch có thể được triển khai trên các máy chủ tại chỗ (on-premise) nhưng phổ biến hơn hết là trên môi trường đám mây. Vào năm 2015, Amazon Web Services đã triển khai Elasticsearch trên đám mây AWS để cho phép các nhà phát triển phần mềm chạy và vận hành các cụm máy chủ Elasticsearch trên môi trường đám mây. Việc triển khai Elasticsearch trên môi trường đám mây AWS là cần thiết bởi nó cho phép các ứng dụng trên hệ sinh thái AWS có thể dễ dàng giao tiếp, và nhà phát triển phần mềm dễ dàng giám sát hiệu năng hệ thống nhờ sự hỗ trợ gián tiếp từ AWS Cloudwatch và nhiều dịch vụ hạ tầng khác.

    Đến năm 2021, Amazon Web Services khởi chạy dự án nguồn mở OpenSearch, như một bản sao chép từ phiên bản 7.10.2 của Elasticsearch. Và OpenSearch được phát triển, bảo trì và quản lý bởi Amazon Web Services. Dịch vụ hạ tầng từ đó cũng được đổi tên thành AWS OpenSearch.

    Author: Ha Huu Linh

  • ANT DESIGN – Điều gì khiến thư viện này vượt “mặt” Material-UI của Google?

    ANT DESIGN – Điều gì khiến thư viện này vượt “mặt” Material-UI của Google?

    anhbia Ant Design – Điều gì khiến thư viện này vượt "mặt" Material-UI của Google?

    Nếu bạn là một Front-End developer và thường xuyên sử dụng những thư viện UI cho các dự án của mình thì chắc chắn các bạn đều sẽ biết đến những cái tên rất quen thuộc như Material-UI (MU) do Google phát triển hay Bootstrap do hai kỹ sư tại Twitter làm ra, tất cả đều là các thư viện hỗ trợ thật sự tuyệt vời về tính Responsive cho các màn hình thiết bị khác nhau, các components Javascript có sẵn.

    Tuy nhiên, bạn biết đó, thời nào cũng sẽ có những anh hùng hào kiệt, một cái tên khác đang phất lên rất nhanh và cũng nhanh chóng được yêu thích của cộng đồng developers React Front-End đó là ANT DESIGN (ANTD) đạt 73,1k sao trên github và đang vượt lên so với Material-UI (70k sao). Có lẽ điều đầu tiên bạn sẽ biết về ANTD đó là thế lực tạo ra nó – Alibaba.

    Vậy ANTD có gì mà có thể vượt lên so với MU – một thư viện UI/UX do Google phát triển rất lâu trước đó?

    Tổng quan bài viết

    ANT DESIGN và ANT DESIGN PRO

    Bạn có phải lần đầu biết đến ANTD? Có khoảnh khắc nào trong những giây phút "chilling" bạn search google với keyword "ant design là gì?" nhưng chắc có lẽ bạn vẫn chưa biết tới cái tên ANT DESIGN PRO nếu chưa bao giờ sử dụng ANTD.

    ANT DESIGNANT DESIGN PRO là hai phần khác nhau của ANTD.

    ANT DESIGN: Dành cho những components đã được viết sẵn, bạn chỉ cần import và tái sử dụng lại những components đó, ví dụ như Carousels, Form, Upload, Modal, Badge,…etc.

    • Điều khiến ANTD trở nên đặc biệt là Document cực kì rõ ràng, các api của components đều có demo và ví dụ đi kèm, bạn có thể xem code trong codeSandbox/codepen/stackblitz. Một số components được ANTD viết với TypeScript, cùng nghía qua một "tí tẹo" document của ANTD nha:

    document

    Nếu những điều này chưa khiến bạn phải "quaoooo" thì hãy xem tới các props của ANTD và khả năng hỗ trợ Customize Base component của nó.

    Bạn biết đấy, đôi khi làm việc với các thư viện, bạn sẽ muốn thay đổi các base components để đáp ứng như cầu của khách hàng/dự án và props của ANTD sẽ là công cụ đắc lực giúp bạn dễ customize hơn và viết code tối giản hơn nhiều.

    Vậy còn ANTD PRO thì sao?

    ANT DESIGN PRO thì khác, nó là một theme gần như hoàn chỉnh dành cho các trang Admin, CMS ( Control Management System). Bạn có thể thấy được những gì mà ANTD PRO có thể hỗ trợ bạn qua <a href="https://preview.pro.ant.design/" target="_blank">website preview</a>.

    • Cài đặt ANTD PRO với npm hoặc yarn:

    install

    Sau khi cài đặt, bạn sẽ thấy rằng điều bạn cần làm là chọn những phần components bạn muốn và bắt đầu gọi API từ phía Back-end hoặc gỉa lập API với file _mock.js có sẵn của ANTD PRO luôn mà không cần phải đau đầu suy nghĩ về UI hay viết code làm sao. Rất tiện lợi đúng không?

    Chưa hết, ANTD PRO còn mang đậm tính "all-in-one", chỉ 1 lần cài đặt mà bạn có thể sử dụng luôn:

    1. Webpack (Webpack giúp tăng tốc độ cho dự án, đóng gói thành một file duy nhất,…)

    2. Less ( một CSS preprocessor giống với SCSS )

    3. Sự tuyệt vời đến từ UmiJS – cái tên bạn … chưa nghe qua bao giờ đúng không?

    UmiJS: chính là một thư viện Plugin enterprise-level cho React. Nghe thì xa tầm với nhưng ý lại nằm trong lời, UmiJS bao hàm bên trong là redux-saga, gọi api thay vì với Axios thì sẽ là Request.

    Ai cũng biết Axios hỗ trợ việc gọi api request tốt và "uy tín" như thế nào nhưng UMI-Request lại hơn thế đấy, cùng xem bảng so sánh của umi-request với Axios và Fetch sau:

    install

    Bạn đã bị thuyết phục chưa? Nếu chưa thì hãy tới mục số II nha.

    WEB COMPONENT vs MOBILE COMPONENT

    Giống như sự hỗ trợ của ANTD dành cho web version thì ANTD cũng đã dành điều đó cho cả <a href="https://mobile.ant.design/docs/react/introduce" target="_blank">mobile version</a>.

    Version dành cho Mobile là cái mà Material UI chưa làm nhưng ANTD đã làm và còn đầu tư chất lượng cho document của mobile không kém cạnh gì cho web version.

    => Có thể thấy ANTD là một thư viện UI được đầu tư, phát triển mạnh mẽ và đến đây chúng ta cũng có thể hiểu tại sao số sao trên github dành cho ANTD lại vượt nhanh hơn cả MU.

    Tips và ví dụ customize sử dụng PROPS của ANTD

    Từ mục I và II chúng ta đều được biết tới sự hỗ trợ "xịn xò" cho các developers React Front-End nhưng lại không biết nhược điểm của nó là gì và tips để sử dụng ANTD hiệu quả đúng không?

    Nhược điểm của ANTD:

    1. Document của ANTD vẫn còn một vài chỗ viết bằng tiếng Trung, đặc biệt là Document của ANTD PRO/UMIJS

    => Việc tiếng Trung còn nhiều khiến cho các developers gặp khó khăn khi muốn customize components hoặc đọc về API/Props hỗ trợ.

    1. Trên các trang như Stackoverflow, dev.to,… có ít các bài được giải thích hoặc hỗ trợ cho ANTD.

    => Vậy nên khi gặp bug, các bạn cũng có thể mất kha khá thời gian để sửa và kiểm tra lại.

    1. Tuỳ chỉnh Theme

    => Khả năng tuỳ chỉnh theme của ANTD không mạnh bằng MU khi chỉ có thể thay đổi các mặc định về màu sắc, kích thước,…

    Tips khi sử dụng ANTD:

    1. Hãy đọc kĩ các phần API, props của ANTD hỗ trợ.

    => Có thể khi đứng trước một vấn đề mà vấn đề đó cần customize một base component của ANTD mà bạn chưa có ý tưởng bắt đầu làm như thế nào, hãy đọc kì api/props mà component đó có, có thể bạn sẽ nhận ra mình đỡ bỏ lỡ một sự kết hợp hay ho đó.

    1. Hãy viết style bằng less

    => ANTD cũng không quá khắt khe cho việc viết style như khi dùng bootstrap nhưng để tối ưu việc style, responsive và sự tương thích với các trình duyệt khác nhau thì hãy viết với less nhé.

    • Ví dụ về customize sử dụng PROPS của ANTD

    Customize dropdown của <Select> với infinity scroll

    Có 5 bước chúng ta cần làm (nguồn: <a href="https://codesandbox.io/s/antd-select-infinite-scroll-90shp?file=/index.js:1649-2578)" target="_blank">click here to open codeSandbox</a>)

    Bước 1: Import các phần cần thiết và viết một component:

    import React from "react";
    import ReactDOM from "react-dom";
    import "antd/dist/antd.css";
    import "./index.css";
    import Select from "antd/lib/select";
    const { Option } = Select;
    
    class LazySelect2Input extends React.PureComponent {
      render() {
        return <div className="App"></div>;
      }
    }
    

    Bước 2: Viết hàm để render mock data:

    // Fetch at most 200
    // On scroll uses a queue
    // Pop
    
    const children = [...Array(20).keys()];
    const range = (start, stop, step) =>
      Array.from({ length: (stop - start) / step + 1 }, (_, i) => start + i * step);
    const handleChange = (value) => console.log(`selected ${value}`);
    

    Bước 3: Khai báo constructor với props, state:

     constructor(props) {
        super(props);
        this.state = {
          lastScrollPos: 0,
          loadingMore: false,
          options: children,
          min: 0,
          search: undefined
        };
      }
    

    Bước 4: Viết function để thực hiện infinity scroll:

    handleScroll = (event) => {
      event.stopPropagation();
      let delta;
      const { options, min, loadingMore } = this.state;
      let newMin = min;
      if (event.wheelDelta) {
        delta = event.wheelDelta;
      } else {
        delta = -1 * event.deltaY;
      }
    
      if (!loadingMore) {
        if (delta < 0) {
          newMin = min + 1;
          if (newMin + 10 >= Math.max(...options)) {
            // Fetch more items when the list is exhausted.
            this.handleFetchMore(Math.max(...options) + 1, 10, newMin);
          }
        } else if (delta > 0 && min >= 1) {
          newMin = min - 1;
        }
    
        return this.setState({ min: newMin });
      }
    };
    
    handleFetchMore = (offset, limit, min) => {
      this.setState(
        {
          loadingMore: true,
          options: [...this.state.options, ...range(offset, offset + limit, 1)],
        },
        () =>
          this.setState({
            loadingMore: false,
            min,
          })
      );
    };
    

    Bước 5: Render và Return:

    render() {
        const { loadingMore, min, options, search } = this.state;
        let currentOptions = options;
        const extraProps = {};
    
        if (search) {
          currentOptions = options.filter(v => v.toString().indexOf(search) >= 0);
          extraProps.open = true;
          extraProps.key = "searching";
        }
        return (
          <div className="App">
            <Select
              mode="multiple"
              style={{ width: "100%" }}
              onChange={handleChange}
              // dropdownRender, filterOption, defaultActiveFirstOption
              // are props for customize of ANTD
              dropdownRender={menu => <div onWheel={this.handleScroll}>{menu}</div>}
              loading={loadingMore}
              onSearch={search => this.setState({ search })}
              filterOption={false}
              defaultActiveFirstOption={false}
              {...extraProps}
            >
              {currentOptions.slice(min, min + 10).map(i => (
                <Option key={i} value={i}>
                  {i}
                </Option>
              ))}
            </Select>
          </div>
        );
      }
    

    => Có thể thấy các props dropdownRender, filterOption, defaultActiveFirstOption đã hỗ trợ cho chúng ta bắt sự kiện scroll khi tag Select xổ xuống các option để việc thực hiện tải thêm data khi người dùng cuộn xuống phần đáy của tag select (popup container).`

    Tổng kết

    Chúng ta đã có một bài đọc khá dài về ANT DESIGN. Hi vọng những gì mình chia sẻ có thể giúp bạn hiểu thêm về nó và có thể dễ dàng bắt đầu sử dụng hơn nha. Cảm ơn các bạn đã đọc.

  • Triển khai CD cho dự án phát triển Website với Gitlab-CI và AWS S3

    Triển khai CD cho dự án phát triển Website với Gitlab-CI và AWS S3

    Article overview

    Giả sử chúng ta phát triển một sản phẩm Website với ReactJS và sử dụng Static Website Hosting của AWS S3. Mỗi lần deploy đều cần thực hiện build source và upload manual.
    Mục tiêu là triển khai CD để thay thế cho các công việc manual không cần thiết và giảm thiểu các sai sót ngoài ý muốn.

    Tổng quan về các công nghệ sử dụng:

    • ReactJS Website
    • Gitlab-CI
    • AWS S3, AWS CLI
    • Môi trường MacOS

    Table of contents

    Chúng ta cần một số bước sau:

    • Liên kết và khởi tạo Runner với Gitlab repository
    • [Cài đặt và cấu hình môi trường tại thiết bị chạy service runner](## Cài đặt và cấu hình môi trường tại thiết bị chạy service runner)
    • [Cấu hình các job CI/CD với .gitlab-ci.xml và Gitlab-CI](## Cấu hình các job CI/CD với gitlab-ci.xml và Gitlab-CI)

    Cài đặt và cấu hình môi trường tại thiết bị chạy service runner

    Giả định ở thiết bị MacOS chạy service runner đã build được Website ReactJS, chúng ta sẽ skip qua phần cài đặt cho ReactJS. Đầu tiên, chúng ta cần cài đặt AWS CLI.
    Sau khi cài đặt xong, ta thực hiện config với thông tin của AWS User có quyền deploy lên AWS S3 với câu lệnh sau:

    $ aws configure
    AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
    AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
    Default region name [None]: us-west-2
    Default output format [None]: json

    Cấu hình các job CI/CD với gitlab-ci.xml và Gitlab-CI

    Đầu tiên, thay vì upload bằng tay các file trong thư mục build/ chúng ta sẽ sử dụng command aws s3 sync như sau:
    aws s3 sync build/ s3://ww95 với ww95 là tên của Bucket đang Host Website.
    Sau đó, chúng ta sẽ cài đặt command package.json như sau:

      "scripts": {
        "build": "react-scripts build", // Build source code để deploy
        "deploy": "aws s3 sync build/ s3://ww95" // Thực hiện deploy lên S3
      },

    Tiếp đó, ta sẽ cấu hình .gitlab-ci.yml để hệ thống tự động deploy khi có thay đổi trên nhánh master.

    stages:
      - Deployment
    deploy:
      stage: Deployment
      before_script: []
      only:
          - master
      allow_failure: true
      script:
        - yarn install
        - yarn build
        - yarn deploy

    Tuy nhiên, khi chạy thực tế trên Gitlab-CI ta sẽ gặp lỗi sau:

    Treating warnings as errors because process.env.CI = true.
    Most CI servers set it automatically. 
    Failed to compile.

    Để giải quyết vấn đề này ta phải setting lại process.env.CI = false bằng hai cách sau:

    • Thay đổi cấu hình .gitlab-ci.yml từ yarn build -> CI=false yarn build.
    • Cài đặt biến môi trường trong Gitlab-CI như ảnh sau

    Sau đó merge code vào master, và hưởng thành quả. Từ giờ các bạn không cần phải deploy bằng tay nữa rồi, chúc các bạn may mắn.

    Authors

    MinhNN44

  • [AWS] Phát triển ứng dụng Lambda bằng Java

    [AWS] Phát triển ứng dụng Lambda bằng Java

    Như các bạn đã biết hiện nay môi trường thực thi sử dụng trong Lambda phần lớn đang sử dụng Node hay Python. Tuy nhiên trên thực tế đôi khi bạn cần sử dụng một môi trường thực thi khác như Java chẳng hạn. Trên thực tế thì AWS cũng đang hỗ trợ khá nhiều môi trường thực thi khác nhau. Có nhiều lý do dẫn tới việc chúng ta phải sử dụng một môi trường thực thi nào đó tuỳ vào tình hình dự án. Trong bài viết này tôi sẽ hướng dẫn các bạn xây dựng ứng dụng Lamba sử dụng môi trường thực thi là Java.

    Các công cụ cần thiết

    Docker

    Chúng ta cần Docker bởi vì công cụ thực thi SAM CLI sẽ sử dụng docker container để thực thi ứng dụng. Bạn thao khảo đường dẫn sau để cài đặt Docker

    SAM

    Chúng ta sẽ sử dụng SAM vì chúng ta cần một môi trường thực thi có thể chạy trên môi trường cục bộ và có thể debug được. Để cài SAM bạn làm theo hướng dẫn sau:

    brew tap aws/tap
    brew install aws-sam-cli
    

    Chúng ta sử dụng brew để cài SAM nên bạn cần cài brew trước. Nếu chưa cài brew thì bạn có thể thao khảo cách cài brew như sau:

    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
    hieunv@HieuNV ~ % brew --version
    Homebrew 2.2.10
    Homebrew/homebrew-core (git revision f0179; last commit 2020-03-22)
    Homebrew/homebrew-cask (git revision 0a88ae; last commit 2020-03-22)
    

    Để kiểm tra xem bạn đã cài đặt thành công chưa, bạn sử dụng lệnh sau:

    hieunv@HieuNV ~ % sam --version
    SAM CLI, version 0.45.0
    

    Trên Windows thì bạn thao khảo đường dẫn này

    Oracle JDK

    Chúng ta sẽ sử dụng môi trường thực thi Java nên việc cài đặt Oracle JDK là đương nhiên đúng không. Các bạn tham khảo cách cài đặt Oracle JDK tại đây nhé.

    Maven

    SAM sẽ sử dụng maven để build nên chúng ta cần cài đặt thêm maven. Để cài đặt Maven các bạn sử dụng lệnh sau:

    brew install --ignore-dependencies maven
    

    Các bạn chú ý, chúng ta cần sử dụng --ignore-dependencies để bỏ qua việc cài đặt Open JDK nhé. Mặc định maven sẽ sử dụng Open JDK. Tuy nhiên chúng ta đã cài đặt Oracle JDK rồi nên không cần cài Open JDK nữa.

    Tài liệu tham khảo:

    Tạo project bằng SAM

    • Tạo một project mới
    hieunv@HieuNV hieunv % sam init -r java11
    Which template source would you like to use?
    	1 - AWS Quick Start Templates
    	2 - Custom Template Location
    Choice: 1
    
    Which dependency manager would you like to use?
    	1 - maven
    	2 - gradle
    Dependency manager: 1
    
    Project name [sam-app]:
    
    Cloning app templates from https://github.com/awslabs/aws-sam-cli-app-templates.git
    
    AWS quick start application templates:
    	1 - Hello World Example: Maven
    	2 - EventBridge Hello World: Maven
    	3 - EventBridge App from scratch (100+ Event Schemas): Maven
    Template selection: 1
    
    -----------------------
    Generating application:
    -----------------------
    Name: sam-app
    Runtime: java11
    Dependency Manager: maven
    Application Template: hello-world
    Output Directory: .
    
    Next steps can be found in the README file at ./sam-app/README.md
    
    • Trước khi thực thi bạn cần build project trước
    hieunv@HieuNV hieunv % cd sam-app
    hieunv@HieuNV sam-app % sam build
    Building resource 'HelloWorldFunction'
    /usr/local/bin/mvn is using a JVM with major version 13 which is newer than 11 that is supported by AWS Lambda. The compiled function code may not run in AWS Lambda unless the project has been configured to be compatible with Java 11 using 'maven.compiler.target' in Maven.
    Running JavaMavenWorkflow:CopySource
    Running JavaMavenWorkflow:MavenBuild
    Running JavaMavenWorkflow:MavenCopyDependency
    Running JavaMavenWorkflow:MavenCopyArtifacts
    
    Build Succeeded
    
    Built Artifacts  : .aws-sam/build
    Built Template   : .aws-sam/build/template.yaml
    
    Commands you can use next
    =========================
    [*] Invoke Function: sam local invoke
    [*] Deploy: sam deploy --guided
    
    • Khởi động ứng dụng (trước khi khởi động bạn cần đảm bảo rằng Docker đang hoạt động)
    hieunv@HieuNV sam-app % sam local start-api
    Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
    You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
    2020-03-22 22:07:45  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
    

    Chúng ta thử truy cập vào http://127.0.0.1:3000/hello bằng Postman. Nếu các bạn chưa chạy lần nào thì sẽ phải chờ hơi lâu một chút để Docker tải image cần thiết.

    start-api

    Trong bài viết này tôi đã hướng dẫn các bạn cách viết một API bằng Lambda sử dụng môi trường thực thi Java. Hy vọng bài viết sẽ giúp ích cho dự án của các bạn.

  • [AWS] Serverless và SAM, bạn chọn dùng cái nào?

    [AWS] Serverless và SAM, bạn chọn dùng cái nào?

    Mình đã viết khá nhiều bài sử dụng Serverless, tại sao mình lại viết bài này. Thực ra mình cũng mới bắt đầu làm AWS Lambda được một thời gian ngắn. Dự án đầu tiên mình làm Lambda thì đã các bạn đi được đã chọn Serverless để phát triển. Dự án thứ hai mình làm với AWS Lambda thì khách hàng đưa cho mình bộ mã nguồn đã được cấu hình sử dụng Serverless. Mọi thứ đều có vẻ ổn cho đến một ngày mình quyết định thử debug Lambda bằng Visual Studio Code. Mọi thứ trở nên phức tạp và mình tìm thấy SAM, dường như nó đã giải quyết vấn đề của mình nên mình quyết định viết bài này để cho các bạn nếu mới đến với thế giới AWS thì có thể dễ dàng lựa chọn thứ mình cần.

    Chọn Serverless hay SAM?

    Dự án đầu tiên mình dùng Serverless và viết bằng JavaScript, mọi thứ đều ổn vì mình chỉ dùng Serverless có kết hợp với Serverless Offline để chạy các hàm Lambda API trên máy tính cá nhân được. Việc debug cũng không gặp trở ngại gì do Serverless Offline có hỗ trợ debug. Thế nhưng đến dự án tiếp theo, ngôn ngữ được chọn làm môi trường thực thi là Python và mình thực sự gặp khó khăn. Mình vẫn có thể chạy được các hàm Lambda trên máy tính cá nhân nhưng không thể debug đươc. Và thế là mình bắt đầu tìm hiểu để giải quyết vấn đề này. Rồi mình tìm thấy SAM và mọi thứ dường như được giải quyết.

    Ngôn ngữ nào được hỗ trợ?

    • Serverless Offline hỗ trợ những ngôn ngữ sau:
      • Python
      • Ruby
      • Node
    • SAM hỗ trợ nhưng ngôn ngữ sau:
      • Python
      • Ruby
      • Node
      • Java
      • .NET Core

    Được hỗ trợ như thế nào?

    • Serverless Offline là plugin được cá nhân phát triển. Nó không phải gói được hỗ trợ chính thức từ AWS.

    • SAM được hỗ trợ chính thức từ AWS.

    Hỗ trợ debug như thế nào?

    • Serverless Offline chỉ hỗ trợ debug với Node.

    • SAM thì có vẻ như đã hỗ trợ tất cả các trình thực thi ở trên. Mình đã thử debug với Java thì thấy vẫn OK.

    Môi trường thực thi

    • Serverless chạy trực triếp trên máy host.
    • SAM thì sử dụng container trong docker để thực thi.

    Các bạn có thể tham khảo hướng dẫn sử dụng Serverless ở các tài liệu sau nhé:

    Kết luận

    SAM dường như có những lợi thế hơn hẳn so với Serverless. Nếu bạn quyết định phát triển bằng Node thì bạn sẽ không gặp nhiều khó khăn khi dùng Serverless hay SAM. Nếu bạn chọn một môi trường thực thi khác như Python hay Ruby hay bất kỳ môi trường nào khác thì lựa chọn SAM sẽ là quyết định sáng suốt hơn đấy. Mình sẽ hướng dẫn các bạn sử dụng SAM trong loạt bài viết về SAM sau nhé.

  • [React] Kết hợp các mẫu lập trình để sử dụng hiệu quả Runtime Type Checking

    [React] Kết hợp các mẫu lập trình để sử dụng hiệu quả Runtime Type Checking

    Như các bạn đã biết, Static Type Checking giúp chúng ta kiểm soát kiểu dữ liệu tốt hơn khi viết mã nguồn. Các bạn có thể sử dụng Flow hoặc TypeScript đều được. Tuy nhiên khi dữ liệu được liên kết với API chẳng hạn, khi đó bạn không thể kiểm soát được giá trị được truyền vào cho biến nào đó. Do đó chúng ta vẫn cần thêm một bước nữa để hạn chế các lỗi tiềm ẩn bằng Runtime Type Checking. Trong bài viết này tôi sẽ hướng dẫn các bạn sử dụng Runtime Type Checking như thế nào cho hiệu quả.

    Runtime Type Checking là gì?

    Runtime Type Checking là quá trình xác minh lại kiểu dữ liệu của giá trị truyền vào cho biến có đúng với kiểu dữ liệu mong muốn hay không. Trong ứng dụng React khi bạn khai báo propTypes cho component nào đó chính là lúc bạn đang định nghĩa kiểu dữ liệu mong muốn. Tại thời điểm thực thi các định nghĩa này sẽ được kiểm tra giúp bạn kiểm soát việc binding dữ liệu từ API vào component có tương thích với nhau hay không.

    Định nghĩa propTypes

    React cung cấp gói prop-types giúp bạn định nghĩa các thuộc tính cần cho Runtime Type Checking. Trước đây thì gói này nằm trong React, hiện tại thì nó đã tách ra thành một gói riêng rồi. Các bạn khai báo propTypes như sau:

    import React from 'react';
    import PropTypes from 'prop-types';
    
    export function Octocat(props) {
      const { login, avatar_url, name } = props;
      return (
        <ul>
          <li>{login}</li>
          <li>{name}</li>
          <li>
            <img src={avatar_url} alt={login} />
          </li>
        </ul>
      );
    }
    
    Octocat.propTypes = {
      login: PropTypes.string,
      avatar_url: PropTypes.string,
      name: PropTypes.string
    };
    

    Trong quá trình thực thi nếu dữ liệu được truyền vào component không đúng với định nghĩa propTypes bạn sẽ nhận được một thông báo tương tự như sau:

    prop-types

    Kết hợp với Spread Attributes

    Bạn có để ý rằng khi chúng ta khai báo giá trị truyền vào trong props thì khi sử dụng component này bạn phải khai báo như sau:

    <Octocat login="abc" avatar_url="def" name="xyz" />
    

    Nếu số lượng thuộc tính trong component này tăng lên thì việc viết mã nguồn sẽ khá là vất vả. Rất may là trong JSX cung cấp cho bạn tính năng Spead Attributes và bạn có thể viết code như sau:

    const data = {login: "abc" avatar_url: "def" name: "xyz"};
    <Octocat {...data} />
    

    Toán tử ... được gọi là toán tử spead.

    Kết hợp với Destructuring Props

    Các bạn có thấy rằng khi khai báo Octocat component tôi đã truyền props như làm tham số của một hàm và sau đó mới gán lại vào các biến. Với Destructuring Props bạn có thể viết mã nguồn đơn giản hơn như sau:

    import React from 'react';
    import PropTypes from 'prop-types';
    
    export function Octocat({ login, avatar_url, name }) {
      return (
        <ul>
          <li>{login}</li>
          <li>{name}</li>
          <li>
            <img src={avatar_url} alt={login} />
          </li>
        </ul>
      );
    }
    
    Octocat.propTypes = {
      login: PropTypes.string,
      avatar_url: PropTypes.string,
      name: PropTypes.string
    };
    

    Tài liệu tham khảo:

    Cám ơn các bạn đã theo dõi bài viết. Hy vọng bài viết sẽ giúp các bạn viết mã nguồn tốt hơn.

  • [React] Static Type Checking là gì? Flow và TypeScript, bạn chọn cách nào?

    [React] Static Type Checking là gì? Flow và TypeScript, bạn chọn cách nào?

    Static Type Checking và Runtime Type Checking là hai thuật ngữ ban đầu cảm giác có vẻ không quen thuộc cho nhưng nó lại lai thứ có lẽ các bạn đang dùng hàng ngày. Như các bạn biết thì JavaScript là ngôn ngữ định kiểu động, nghĩa là kiểu dữ liệu chỉ được xác định tại thời điểm thực thi. Do đó khi lập trình chúng ta không cần xác định kiểu dữ liệu cho biến. Điều này vô hình chung làm cho mã nguồn của chúng ta trở nên khó đọc hơn và khó debug hơn nữa. Chính vì vậy việc xác định kiểu dữ liệu giúp cho chúng ta kiểu soát và debug dễ dàng hơn. Trong bài viết này tôi sẽ chia sẻ với các bạn cách công đồng đang làm để hỗ trợ hai kiểu kiểm tra kiểu dữ liệu này.

    Static Type Checking là gì?

    Hiện nay có hai xu hướng để kiểm tra kiểu dữ liệu tĩnh là FlowTypeScript. Static Type Checking sẽ phân tích mã nguồn tĩnh trước khi biên dịch mã nguồn sang mã JavsScript(ES5) là mã nguồn mà trình duyệt có thể đọc được. Vậy thì chúng khác nhau ở điểm nào?

    Flow

    Flow là một phần mở rộng của Babel cung cấp cho bạn việc kiểm tra kiểu dữ liệu tĩnh bằng cách sử dụng các chỉ thị bằng comment mà bạn thêm vào mã nguồn.

    Ví dụ cách sử dụng flow như bên dưới:

    // @flow
    function square(n: number): number {
      return n * n;
    }
    
    square('2'); // Error!
    

    TypeScript

    TypeScript là ngôn ngữ dựa trên JavaScript được Microsoft phát triển. TypeScript khác với Flow là nó không phân tích mã nguồn dự trên các chỉ thị bằng comment. Kiểu dữ liệu được hỗ trợ từ trong ngôn ngữ luôn. Dữ liệu được kiểm tra tại thời điểm phân tích cú pháp của chương trình dịch. Ngoài ra thì TypeScript hỗ trợ rất mạnh OOP nên nó rất phù hợp với các bạn quen thuộc với các ngôn ngữ định kiểu như Java và C#.

    const add = (x: number, y: number) => {
      return x + y;
    };
    

    Sử dụng Static Type Checking có lợi ích gì?

    • Khi sử dụng Static Type Checking bạn được hỗ trợ việc kiểm soát dữ liệu tại thời điểm viết mã nguồn, tất cả việc gán hoặc gọi hàm với kiểu dữ liệu không phù hợp sẽ được phát hiện tại thời điểm này.

    • IDE hỗ trợ việc kiểm soát kiểu dữ liệu giúp bạn viết code nhanh hơn và sai sốt ít hơn.

    • Mã nguồn dễ đọc hơn cho người mới.

    Bạn mất gì khi sử dụng Static Type Checking?

    Static Type Checking mang những lợi ích nhất định trong việc kiểm soát kiểu dữ liệu, tuy nhiên nó cũng có những nhiểm điểm:

    • Bạn phải viết code nhiều hơn thì mới kiểm soát được kiểu dữ liệu, việc này đương nhiên sẽ tốn công sức. Tuy nhiên so với việc mất thời gian vào debug thì có lẽ nó vẫn tốt hơn.

    • Bạn vẫn không kiểm soát được kiểu dữ liệu tại thời điểm chạy chương trình, lý do là nếu chương trình của bạn có liên kết với API thì không có gì đảm bảo kiểu dữ liệu của bạn chạy đúng với backend cả. Đương nhiên cái này là vấn đề không thể giải quyết được rồi. Tôi sẽ hướng dẫn các bạn giải quyết vấn đề này trong bài viết Runtime Type Checking sau nhé.

    Kết luận

    Đây chỉ là kết luận mang tính cá nhân của mình :). Các bạn tự suy ngẫm để đưa ra lựa chọn phù hợp nhé.

    • Việc viết theo Flow có cảm giác không tự nhiên lắm, thường xuyên phải thêm comment khiến bạn khá mất thời gian. Thêm một vấn đề nữa là lúc viết code sử dụng Flow khá tốn tài nguyên của máy. Về vấn đề này thì mình thấy TypeScript có vẻ ổn hơn.

    • TypeScript không đơn giản là một sự mở rộng của JavaScript, Microsoft đã thêm vào nhiều thứ hơn thế và nó cũng không dựa trên các tiêu chuẩn của ES6, ES7, … (có support ES6, ES7, … nhưng có những thêm vào nhiều thứ khác nữa). Về điểm này thì Flow có vẻ tốt hơn.

    • Sau sự ra đời của React Hooks thì mọi thế mạnh của TypeScript gần như không còn nữa so với Flow, bản thân mình cũng không dùng TypeScript nữa.

    • Một điểm quan trong nữa là cả TypeScript và Flow đều không thể xác mình kiểu dữ liệu lúc thực thi nên bạn vẫn cần Runtime Type Checking.

    Các bạn có dùng Static Type Checking không? Mình thì đã không dùng nữa và chuyển sang xác mình toàn bộ bằng Runtime Type Checking rồi. Thời gian tốn cho debug cũng không phát sinh nhiều so với việc phải viết mã nguồn xác định kiểu dữ liệu.

    Cảm ơn các bạn đã theo dõi bài viết. Các bạn cùng chờ bài viết mình hướng dẫn về Runtime Type Checking nhé.

  • [React] Sử dụng mẫu container trong React như thế nào?

    [React] Sử dụng mẫu container trong React như thế nào?

    Chắc hẳn ai cũng biết tới nguyên lý đầu tiên của SOLID đó là trách nhiệm đơn nhất. Nghĩa là mỗi một đối tượng chỉ làm một nhiệm vụ duy nhất. Đơn giản vì với tính đơn nhất chúng ta sẽ dễ dàng kiểm soát mã nguồn hơn (cho cả người code lần người review). Chưa kể tới việc với tính đơn nhất thì mã nguồn của bạn cũng trở nên trong sáng hơn. Với anh em lập trình React cũng vậy, bài toán khá đau đầu là làm sao tách logic ra khỏi phần mã nguồn kết xuất HTML. Cách làm như vậy giúp chúng ta thay vì viết một React Component để là cả hai việc thì bây giờ chúng ta sẽ viết hai React Component để làm hai nhiệm vụ khác nhau. Trong bài viết này tôi sẽ hướng dẫn các bạn sử dụng mấu container để làm việc đó.

    Container component là gì?

    Chúng ta không có một định nghĩa chính xác nào về container component cả, container component đơn giản là một React Component làm nhiệm vụ goi API để lấy dữ liệu và sau đó truyền nó xuống một React Component để kết xuất mã HTML.

    Định nghĩa container

    Để các bạn dễ hình dung tôi sẽ viết thử một container, container này sẽ thực hiện lấy thông tin của octocat

    src/containers/OctocatContainer.jsx

    import React, { useState, useEffect } from 'react';
    import { Octocat } from '../components/Octocat';
    export function OctocatContainer() {
      const [octocat, setOctocat] = useState(null);
    
      useEffect(() => {
        async function getOctocat() {
          // gọi hàm fetch với phướng thực mặc định là GET
          const response = await fetch('https://api.github.com/users/octocat');
          const body = await response.json();
          // sử dụng React Hook để thông tin octocat vào state
          setOctocat(body);
        }
    
        getOctocat();
      }, []); // tham số mảng rỗng thể hiện hàm effect không phụ thuộc vào đối tượng nào, nó tương đương với hàm componentDidMount
    
      return <Octocat {...octocat} />;
    }
    

    Trong ví dụ trên tôi có sử dụng thêm JSX spread attributes để tránh việc phải khai báo props cho Octocat component. Mẫu này đặc biệt có ý nghĩa đối với các component mà bạn cần khai báo nhiều props.

    Định nghĩa component để kết xuất mã HTML

    Sau khi có dữ liệu rồi thì chúng ta sẽ kết xuất nó ra HTML như sau:

    src/components/Octocat/Octocat.jsx

    import React from 'react';
    
    export function Octocat(props) {
      const { login, avatar_url, name } = props;
      return (
        <ul>
          <li>{login}</li>
          <li>{name}</li>
          <li>
            <img src={avatar_url} alt={login} />
          </li>
        </ul>
      );
    }
    

    Như các bạn thấy, component trên chỉ làm nhiệm vụ bind dữ liệu lên màn hình mà thôi.

    Cùng xem kết quả nhé 🙂

    octocat

  • [React] Tôi đã làm toast component như thế nào?

    [React] Tôi đã làm toast component như thế nào?

    Với bất kỳ ứng dụng nào việc hiển thị thông báo lỗi là không thể thiếu được. Các bạn có thể có rất nhiều cách làm để hiển thị được các thông báo lỗi này. Trong bài viết này tôi xin chia sẻ cách làm của tôi để các bạn cùng tham khảo nhé.

    Với bất kỳ ứng dụng nào việc hiển thị thông báo lỗi là không thể thiếu được. Các bạn có thể có rất nhiều cách làm để hiển thị được các thông báo lỗi này. Trong bài viết này tôi xin chia sẻ cách làm của tôi để các bạn cùng tham khảo nhé.

    Mục đích của tôi khi viết component này phải đảm bảo nó dễ dùng như hàm window.alert() vậy. Trong bài viết này tôi sẽ sử dụng Material UI để hiển thị thông báo lỗi.

    Toast Context

    Vì chúng ta cần hàm hiển thị thông báo lỗi có thể sử dụng như window.alert() nên chúng ta cần khai báo một nơi lưu trữ để từ đó chúng ta có thể gọi được ở bất kỳ đâu trong ứng dụng. Có nhiều bạn sẽ nghĩ tới Redux nhưng trong bài viết này tôi sẽ sử dụng React Context API để làm việc đó. Chúng ta bắt đầu bằng việc khai báo ToastContext nào.

    src/contexts/toast.js

    import React, { useContext } from 'react';
    
    export const ToastContext = React.createContext();
    
    export const useToast = () => useContext(ToastContext);
    

    Toast Provider

    Trong React Context API có định nghĩa Provider để khi có sử thay đổi trong component thì các component bên trong có thể nhận biết sử thay đổi đó và phản anh kết quả thay đổi lên màn hình. Ở đây chúng ta cần hiển thị message lỗi mỗi khi có component nào đó thông báo về việc hiển thị message lỗi.

    import React, { useState } from 'react';
    import { Snackbar } from '@material-ui/core';
    import { Alert } from '@material-ui/lab';
    import { ToastContext } from '../contexts/toast';
    
    export const ToastProvider = (props) => {
      const { children } = props;
      const [state, setState] = useState({ isOpen: false });
    
      const show = (message) => {
        setState({ isOpen: true, message });
      };
    
      const hide = () => setState({ isOpen: false });
    
      const error = (message) => {
        show({ type: 'error', text: message });
      };
    
      const warn = (message) => {
        show({ type: 'warning', text: message });
      };
    
      const info = (message) => {
        show({ type: 'info', text: message });
      };
    
      const success = (message) => {
        show({ type: 'success', text: message });
      };
      const { isOpen, message } = state;
      return (
        <ToastContext.Provider
          value={{
            error: error,
            warn: warn,
            info: info,
            success: success,
            hide: hide
          }}>
          {children}
          {message && (
            <Snackbar open={isOpen} autoHideDuration={6000} onClose={hide}>
              <Alert elevation={6} variant="filled" onClose={hide} severity={message.type}>
                {message.text}
              </Alert>
            </Snackbar>
          )}
        </ToastContext.Provider>
      );
    };
    

    Sử dụng các hàm error, warn, info, success để hiển thị thông báo

    Để sử dụng được các hàm này trong ứng dụng bạn cần khai báo nó trong component chính của ứng dụng

    import React from 'react';
    import { ToastProvider } from './providers/ToastProvider';
    import { useToast } from './contexts/toast';
    import './App.css';
    
    function ButtonList() {
      const { error, warn, info, success } = useToast();
      return (
        <>
          <button onClick={() => error('error message!')}>error</button>
          <button onClick={() => warn('warn message!')}>warn</button>
          <button onClick={() => info('info message!')}>info</button>
          <button onClick={() => success('success message!')}>success</button>
        </>
      );
    }
    
    function App() {
      return (
        <ToastProvider>
          <ButtonList />
        </ToastProvider>
      );
    }
    export default App;
    

    Cám ơn các bạn đã theo dõi bài viết. Hy vọng bài viết đã cung cấp cho các bạn thêm một các để hiển thị thông báo tốt hơn cho ứng dụng của bạn.