Author: Hieu Nguyen

  • [React] Class Component và Function Component, bạn chọn viết theo cách nào?

    [React] Class Component và Function Component, bạn chọn viết theo cách nào?

    Trong thế giới React thì chắc hẳn ai cũng biết đến Class Component và Function Component. Tuy nhiên có thể có những hiểu nhầm về hai loại component này. Trong bài viết này tôi sẽ thử so sánh hai cách viết này để giúp bạn có thể lựa chọn viết theo cách nào. Chúng ta cùng bắt đầu nhé.

    Cú pháp

    Khác nhau đầu tiên giữa Class ComponentFunction Component thể hiện ngay ở cách khai báo.

    Class Component

    import React, { Component } from 'react';
    
    class TestComponent extends Components {
      // phương pháp này bắt buộc phải khai báo hàm để kết xuất mã HTML
      render() {
        return <div>TestComponent</div>;
      }
    }
    

    Cách khai báo này khá quen thuộc với các bạn có nền tảng lập trình hướng đối tượng (OOP). Với những bạn mới học React hoặc chuyển sang học React thì phương pháp tiếp cận này có vẻ phù hợp và dễ hiểu.

    Function Component

    import React from 'react';
    
    export function TestComponent() {
      // phương pháp xem kết xuất mã HTML như là giá trị trả về của hàm
      return <div>TestComponent</div>;
    }
    

    Function component sử dụng cách tiếp cận khác đó là sử dụng pure function để khai báo component. Ban đầu function component được sử dụng để viết các component chỉ với mục đích kết xuất HTML mà thôi. Với các component với theo hướng tiếp cận này thì bạn sẽ không can thiệp được vào lifecycle của component. Do đó nó thướng được biết đến với tên gọi Stateless Component.

    Props

    Class Component

    props trong Class Component được xem như giá trị truyển vào cho hàm khởi tạo class.

    import React, { Component } from 'react';
    
    class TestComponent extends Components {
      constructor(props) {
        super(props); // bắt buộc phải có dòng này để gọi hàm khởi tạo của class cha nhé
      }
    
      render() {
        return <div>TestComponent</div>;
      }
    }
    

    Function Component

    props trong Function Component thì được xem như là giá trị truyền vào hàm pure function khi định nghĩa component.

    import React from 'react';
    
    export function TestComponent(props) {
      return <div>TestComponent</div>;
    }
    

    Định nghĩa defaultPropspropTypes thì không có sự khác biệt giữa Class Component và Function Component.

    TestComponent.defaultProps = {};
    
    TestComponent.propTypes = {};
    

    State

    Trước khi React Hooks ra đời thì như đã nói ở trên Function Component con được biết đến với tên gọi Stateless Component. Nghĩa là nó không có state. Khi React Hooks ra đời thì Function Component cũng có state của riêng nó.

    Class Component

    state trong Class Component dược định nghĩa như sau:

    import React, { Component } from 'react';
    
    class TestComponent extends Components {
      constructor(props) {
        super(props);
        // khởi tạo giá trị state
        this.state = { isLoading: false };
      }
    
      render() {
        return <div>TestComponent</div>;
      }
    }
    

    Khi muốn thay đổi giá trị state bạn gọi phương thức setState của component:

    this.setState((state) => ({ isLoading: true }));
    

    Function Component

    state trong Function Component được định nghĩa như sau:

    import React, { useState } from 'react';
    
    export function TestComponent(props) {
      // giá trị khởi tạo state được truyền vào trong useState hook
      const [state, setState] = useState({ isLoading: false });
    
      return <div>TestComponent</div>;
    }
    

    Các bạn để ý hàm useState trả về giá trị của component state trong biến state và hàm setState. Khi muốn thay đổi giá trị của state thì bạn có thể gọi hàm setState.

    setState({ isLoading: true });
    

    Component Lifecycle

    Với Class component các bạn sẽ thấy component lifecycle khá rõ ràng với các hàm như componentDidMount, componentDidUpdate. Function Component thì không như vậy, toàn bộ việc sử lý lifecycle đều thông qua useEffect hook.

    // componentDidMount
    useEffect(() => {
      return () => {}; // componentWillUnmount
    }, []);
    
    // componentDidUpdate
    useEffect(() => {
      return () => {}; // componentWillUnmount
    }, [state]);
    

    Như các bạn thấy thì componentDidMountcomponentDidUpdate không chỉ định rõ khi nào thì hàm được gọi. Việc gọi hàm thì tự chúng ta hiểu dựa theo lifecycle của React component thôi. Với Function Component và useEffect thì khác, bạn có thể thấy [][state] chị định rõ ràng đối tượng phụ thuộc mà khi chúng thay đổi thì hàm truyển vào useEffect sẽ được gọi. Có vẻ như nó lại tường mình hơn là các hàm lifecycle trong class Component.

    Sau sự có mặt của TypeScript thì có lẽ Class Component và React Hooks thì có lẽ bạn Class Component chiếm ưu thế tuyệt đối. Thời điểm đó anh em thi nhau viết Class Component với TypeScript và tôi cũng thế :). Ngay đến cả facebook cũng support TypeScript với create-react-app nữa cơ mà. Thế nhưng khi React Hooks xuất hiện thì có vẻ gió đã đổi chiều, việc xử lý state và lifecycle với hook có vẻ đơn giản hơn rất nhiều. Các bạn thì thấy thế nào. Bạn sẽ chọn cách nào với dự án của mình?

  • [React] Những điều có thể bạn chưa biết về Function Component

    [React] Những điều có thể bạn chưa biết về Function Component

    Chắc hẳn khi đọc bài này bạn cũng đã từng nghe nói đến các khái niệm Function Component và Class Component hay Stateless Component và Stateful Component. Cũng có thể bạn cũng từng nghe Function Component là Stateless Component. Vậy nó có thực sự là như thế. Trong bài viết này chúng ta cùng nhau tìm hiểu nhé.

    Nội dung bài viết gồm các phần sau:

    • Function Component là gì?
    • Function Component có props không?
    • Function component và sức mạnh đến từ React Hook

    Function component là gi?

    Function component là React Component được viết bằng Pure Function. Dễ hiểu hơn thì nó được viết theo dạng sau:

    export function Counter() {
      return <div>count</div>;
    }
    

    Function Component có props không?

    Có bạn sẽ đặt câu hỏi sau khi nhìn thấy cách viết trên, Function Component có props không? Câu trả lời là có. Bạn có thể truyền props cho component như sau:

    import React from 'react';
    
    export function Counter(props) {
      return <div>{props.count}</div>;
    }
    

    Và bạn vẫn có thể khai báo propTypes cho component như bình thường

    Counter.propTypes = {
      count: PropTypes.number
    };
    

    Mọi thứ đều ổn đến lúc này và có lẽ bạn cũng có nghe ai đó nhắc đến Stateless component. Có thể bạn cũng nghe ai đó nói Function Component là Stateless component. Vậy thì Function component có thực sự là Stateless không?

    Function component và sức mạnh đến từ React Hook

    Trước khi React Hooks ra đời, Function component thực sự là Stateless component. Đơn giản bởi bạn chẳng có cách nào thêm state cho nó cả. Function component khi đó chỉ có thể được dùng để render HTML với props truyền vào mà thôi.

    Khi React Hooks ra đời thì lại là câu chuyện khác. Function component có thể có state của riêng nó. Vậy làm sao để thêm state cho Function component? Với useState bạn có thể thêm state cho nó.

    Thay đổi một chút đoạn source code bên trên như sau là Functiona component của bạn đã support state rồi đấy.

    import React, { useState } from 'react';
    import PropTypes from 'prop-types';
    
    export function Counter() {
      const [count, setCount] = useState(0);
    
      return (
        <div>
          <button onClick={() => setCount(count - 1)}>-</button>
          {count}
          <button onClick={() => setCount(count + 1)}>+</button>
        </div>
      );
    }
    
    Counter.propTypes = {
      count: PropTypes.number
    };
    

    Kết luận lại là Function Component sẽ là Stateless Component nếu không có sự trợ giúp của React Hooks. Với React Hooks thì Function Component đã có toàn bộ sức mạnh như Class Component rồi.

    Cám ơn các bạn đã dành thời gian theo dõi bài viết. Hy vọng bày viết sẽ giúp ích cho các bạn.

  • [MacOS] Hướng dẫn cài đặt Oracle JDK

    [MacOS] Hướng dẫn cài đặt Oracle JDK

    Mặc định thì Oracle JDK sẽ được chọn cài đặt trên MacOS. Do đó nếu muốn sử dụng Oracle JDK thì bạn cần phải cài đặt lại. Trong bài viết này tôi sẽ hướng dẫn các bạn cài đặt Oracle JDK.

    Homebrew

    • Nếu bạn chưa cài đặt brew thì có thể sử dụng lệnh sau để tiến hành cài đặt
    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
    
    • Nếu đã cài đặt rồi thì tiến hành update lastest brew như sau:
    hieunv@HieuNV ~ % brew update && brew upgrade
    Updated 1 tap (homebrew/core).
    ==> New Formulae
    swift-sh
    ==> Updated Formulae
    apache-spark               jetty                      xcodegen
    docker-slim                vim                        zsh-syntax-highlighting
    Updating Homebrew...
    

    Kiểm tra caskjava

    brew cask info java
    

    Nếu homebrew/cask chưa được cài đặt thì nó sẽ tự động cài đặt luôn

    hieunv@HieuNV ~ % brew cask info java
    ==> Tapping homebrew/cask
    Cloning into '/usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask'...
    remote: Enumerating objects: 3655, done.
    remote: Counting objects: 100% (3655/3655), done.
    remote: Compressing objects: 100% (3648/3648), done.
    remote: Total 3655 (delta 26), reused 510 (delta 5), pack-reused 0
    Receiving objects: 100% (3655/3655), 1.23 MiB | 215.00 KiB/s, done.
    Resolving deltas: 100% (26/26), done.
    Tapped 1 command and 3543 casks (3,660 files, 4.0MB).
    java: 13.0.2,8:d4173c853231432d94f001e99d882ca7
    https://openjdk.java.net/
    Not installed
    From: https://github.com/Homebrew/homebrew-cask/blob/master/Casks/java.rb
    ==> Name
    OpenJDK Java Development Kit
    ==> Artifacts
    jdk-13.0.2.jdk -> /Library/Java/JavaVirtualMachines/openjdk-13.0.2.jdk (Generic Artifact)
    
    • Nếu đã cài đặt rồi bạn sẽ nhận được thông tin về phiên bản java đã được cài đặt
    hieunv@HieuNV ~ % brew cask info java
    java: 13.0.2,8:d4173c853231432d94f001e99d882ca7
    https://openjdk.java.net/
    Not installed
    From: https://github.com/Homebrew/homebrew-cask/blob/master/Casks/java.rb
    ==> Name
    OpenJDK Java Development Kit
    ==> Artifacts
    jdk-13.0.2.jdk -> /Library/Java/JavaVirtualMachines/openjdk-13.0.2.jdk (Generic Artifact)
    

    Tiến hành cài đặt Oracle JDK sử dụng brew cask

    hieunv@HieuNV ~ % brew cask install oracle-jdk
    ==> Caveats
    Installing oracle-jdk means you have AGREED to the license at:
      https://www.oracle.com/technetwork/java/javase/terms/license/javase-license.html
    
    ==> Downloading https://download.oracle.com/otn-pub/java/jdk/13.0.2+8/d4173c8532
    ==> Downloading from https://download.oracle.com/otn-pub/java/jdk/13.0.2+8/d4173
    ######################################################################## 100.0%
    ==> Verifying SHA-256 checksum for Cask 'oracle-jdk'.
    ==> Installing Cask oracle-jdk
    ==> Running installer for oracle-jdk; your password may be necessary.
    ==> Package installers may write to any location; options such as --appdir are i
    Password:
    installer: Package name is JDK 13.0.2
    installer: Installing at base path /
    installer: The install was successful.
    ?  oracle-jdk was successfully installed!
    

    Kiểm tra phiên bản java sau khi cài đặt

    hieunv@HieuNV ~ % java --version
    java 13.0.2 2020-01-14
    Java(TM) SE Runtime Environment (build 13.0.2+8)
    Java HotSpot(TM) 64-Bit Server VM (build 13.0.2+8, mixed mode, sharing)
    
    hieunv@HieuNV ~ % javac --version
    javac 13.0.2
    

    setting JAVA_HOME

    Thêm export JAVA_HOME=$(/usr/libexec/java_home) vào ~/.zshrc

    echo 'export JAVA_HOME=$(/usr/libexec/java_home)' >> ~/.zshrc
    

    Kiểm tra biến JAVA_HOME

    Đóng Termial sau đó bật lại và kiểm tra biến JAVA_HOME

    hieunv@HieuNV libexec % echo $JAVA_HOME
    /Library/Java/JavaVirtualMachines/jdk-13.0.2.jdk/Contents/Home
    

    Như vậy là bạn đã tiến hành cài đặt thành công Oracle Java rồi.
    Tài liệu tham khảo
    https://emcorrales.com/blog/install-oracle-jdk-macos-homebrew

  • Mô phỏng AWS Lambda & API Gateway bằng Serverless Offline

    Mô phỏng AWS Lambda & API Gateway bằng Serverless Offline

    Khi phát triển ứng dùng bằng AWS Lambda không phải lúc nào chúng ta cũng có thể phát triển trực tiếp trên AWS được. Do đó việc giả lập môi trường AWS để có thể chạy được Lambda và API Gateway là cần thiết. Nó không chỉ giúp chúng ta có thể học mà còn giúp cho quá trình phát triển nhanh hơn. Trong bài viết này tôi sẽ hướng dẫn các bạn giả lập AWS Lambda và API Gateway bằng Serverless Offline

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

    Trước tiên bạn cần cài đặt các tool cần thiết, bạn có thể tham khảo hướng dẫn cài đặt trong các bài viết sau:

    Bạn có thể dùng lệnh sau để cài serverless

    hieunv@HieuNV lambda % yarn global add serverless
    yarn global v1.22.0
    [1/4] ?  Resolving packages...
    [2/4] ?  Fetching packages...
    [3/4] ?  Linking dependencies...
    [4/4] ?  Building fresh packages...
    success Installed "[email protected]" with binaries:
          - serverless
          - slss
          - sls
    ✨  Done in 14.23s.
    

    Tạo một project mới

    • Tạo project với yarn
    hieunv@HieuNV hieunv % mkdir lambda
    hieunv@HieuNV hieunv % cd lambda
    hieunv@HieuNV lambda % yarn init
    yarn init v1.22.0
    question name (lambda):
    question version (1.0.0):
    question description:
    question entry point (index.js):
    question repository url:
    question author:
    question license (MIT):
    question private:
    success Saved package.json
    ✨  Done in 3.53s.
    
    • Cài đặt serverless-offline
    hieunv@HieuNV lambda % yarn add serverless-offline -D
    
    • Cài đặt serverless-python-requirements để viết lambda handler bằng python
    hieunv@HieuNV lambda % yarn add serverless-python-requirements -D
    

    Cấu hình serverless.yml

    serverless.yml

    service: lambda
    
    frameworkVersion: '>=1.1.0 <2.0.0'
    
    provider:
      name: aws
      runtime: python3.7
    custom:
      serverless-offline:
        port: 4000
    plugins:
      - serverless-offline
      - serverless-python-requirements
    

    Cấu hình lambda handler đầu tiên trong serverless.yml

    Chúng ta tạo một Rest API sử dụng lambda bằng cách thêm đoạn sau vào file serverless.yml

    functions:
      test:
        handler: src.api.test.lambda_handler
        events:
          - http:
              method: get
              path: api/test
              cors: true
    

    Ở đây chúng ta tạo ra một Rest API với phướng thức GET và path /api/test. Các bạn nhìn thấy handler: src.api.test.lambda_handler đúng không. Đây là cấu hình hàm lamda sẽ được gọi bởi API Gateway

    Viết code cho lambda handler

    src/api/test.py

    import json
    
    
    def lambda_handler(event, context):
        headers = {"Access-Control-Allow-Origin": "*", "Accept": "application/json"}
        return {
            "statusCode": 200,
            "headers": headers,
            "body": json.dumps({"status": "success", "data": {}}),
        }
    

    Tạo script để run server

    Thêm đoạn sau vào package.json

        "scripts": {
            "start": "sls offline start"
        },
    

    Giờ thì chạy thôi nào các thanh niên

    hieunv@HieuNV lambda % yarn start
    yarn run v1.22.0
    $ sls offline start
    Serverless: Starting Offline: dev/us-east-1.
    
    Serverless: Routes for test:
    Serverless: GET /api/test
    Serverless: POST /{apiVersion}/functions/lambda-dev-test/invocations
    
    Serverless: Offline [HTTP] listening on http://localhost:4000
    Serverless: Enter "rp" to replay the last request
    

    Dùng Postman để call api vừa tạo nhé:

    Cám ơn các bạn đã theo dõi bài viết. Hy vọng bài viết có thể giúp các bạn tiếp tục học và làm việc cùng với AWS Lambda và API Gateway trong các dự án của mình.

  • [React] Tôi đã tạo thư viện React Component như thế nào

    [React] Tôi đã tạo thư viện React Component như thế nào

    Trong bài viết này tôi sẽ hướng dẫn các bạn tạo ra một thư viện component của riêng mình. Bạn cũng có thể public thư viện nếu thích. Nhiều bạn sẽ tự hỏi rằng có nhiều thư viện lắm rồi thì viết thêm làm chi. Mục đích mình viết bài này để có thể tự tạo ra một package gồm những common component có thể sử dụng lại được. Kết hợp với việc dùng mono repository gồm nhiều packages trong yarn workspaces, các bạn có thể tách code của mình thành các package cho dễ quản lý. Chúng ta cùng bắt đầu nhé.

    Thêm các dependencies cần thiết

    • react & react-dom React component mà nên cần 2 thư viện này là đương nhiên rồi.
    hieunv@HieuNV uikit % yarn add react react-dom
    yarn add v1.22.0
    [1/4] ?  Resolving packages...
    [2/4] ?  Fetching packages...
    [3/4] ?  Linking dependencies...
    [4/4] ?  Building fresh packages...
    success Saved lockfile.
    success Saved 3 new dependencies.
    info Direct dependencies
    ├─ [email protected]
    └─ [email protected]
    info All dependencies
    ├─ [email protected]
    ├─ [email protected]
    └─ [email protected]
    ✨  Done in 7.91s.
    
    yarn add v1.22.0
    info No lockfile found.
    [1/4] ?  Resolving packages...
    [2/4] ?  Fetching packages...
    [3/4] ?  Linking dependencies...
    [4/4] ?  Building fresh packages...
    ✨  Done in 15.05s.
    
    hieunv@HieuNV uikit % yarn add react-scripts -D
    yarn add v1.22.0
    [1/4] ?  Resolving packages...
    [2/4] ?  Fetching packages...
    [3/4] ?  Linking dependencies...
    [4/4] ?  Building fresh packages...
    ✨  Done in 41.42s.
    

    Chúng ta sẽ sử dụng webpack config đã được config trong react-scripts

    Cấu hình styleguidist

    styleguidist.config.js

    module.exports = {
      propsParser: require('react-docgen').parse,
      webpackConfig: require('react-scripts/config/webpack.config')
    };
    

    Thêm script run styleguidist

    package.json

    {
      "name": "uikit",
      "version": "1.0.0",
      "main": "index.js",
      "license": "MIT",
      "scripts": {
        "dev": "npx styleguidist server"
      },
      "devDependencies": {
        "react-scripts": "^3.4.0",
        "react-styleguidist": "^10.6.2"
      },
      "dependencies": {
        "react": "^16.13.0",
        "react-dom": "^16.13.0"
      }
    }
    

    Khởi động dev server thôi nào

    hieunv@HieuNV uikit % yarn dev
    yarn run v1.22.0
    $ npx styleguidist server
    Loading webpack config from:
    /Users/hieunv/Projects/hieunv/uikit/node_modules/react-scripts/config/webpack.config.js
    
    ℹ 「wds」: Project is running at http://localhost:6060/
    ℹ 「wds」: webpack output is served from undefined
    ℹ 「wds」: Content not from webpack is served from /Users/hieunv/Projects/hieunv/uikit
    You can now view your style guide in the browser:
    
      Local:            http://localhost:6060/
      On your network:  http://192.168.1.11:6060/
    

    Sau khi khởi động server xong các bạn truy cập và link http://192.168.1.11:6060/

    Do chưa có component nào được định nghĩa nên các bạn sẽ nhận được thông báo như trên.

    Bắt đầu viết component đầu tiên thôi nào

    styleguidist sẽ tự động đọc các components trong thư mục src/components/*README.md trong thư mục component tương ứng để generate ra document sử dụng react-docgen trên styleguidist server

    src/component/Button/Button.jsx

    module.exports = {
      propsParser: require('react-docgen').parse,
      webpackConfig: require('react-scripts/config/webpack.config')
    };
    

    src/component/Button/README.md

    <Button>Test Button</Button>
    

    Chúng ta truy cập lại vào http://192.168.1.11:6060/ sẽ thấy component được sử dụng để test và có cả document nữa luôn.

    Styleguidist server với react-docgen

    Export component ra bên ngoài để có thể sử dụng ở package khác

    src/index.js

    export * from './components/Button/Button';
    
    

    Cám ơn các bạn đã theo dõi bài viết. Hy vọng bài viết sẽ giúp ích cho các dự án của bạn.

  • [React] Loading indicator with React Context API & Hooks

    [React] Loading indicator with React Context API & Hooks

    Loading indicator có lẽ là component huyền thoại mà dự án nào cũng cần đến. Hôm nay mình sẽ hướng dẫn các bạn viết nó thành dạng global component để có thẻ được gọi tại bất kỳ đâu trong ứng dụng của bạn. Chúng ta cùng bắt đầu nhé.

    Để hiểu thêm về React Context API & Hooks các bạn tham khảo thêm tại đây:

    Chúng ta bắt đầu bằng việc tạo một dự án mới sử dụng React nhé. Trong bài viết này mình sẽ sử dụng create-react-app cùng với yarn để tạo project mới. Các bạn xem hướng dẫn cài yarn tại đây nhé.

    Tạo một project mới

    • Cài create-react-app package:
    hieunv@HieuNV ~ % yarn global add create-react-app
    yarn global v1.22.0
    [1/4] ?  Resolving packages...
    [2/4] ?  Fetching packages...
    [3/4] ?  Linking dependencies...
    [4/4] ?  Building fresh packages...
    success Installed "[email protected]" with binaries:
          - create-react-app
    ✨  Done in 14.08s.
    
    • Sau khi cài xong thì các bạn tạo project mới như sau:
    yarn create react-app loading
    
    Cấu trúc project được tạo bằng create-react-app

    Tạo React context mới để lưu trạng thái của Loading component

    context/loading.js

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

    Tạo LoadingProvider để lưu trạng thái loading như là một global state.

    providers/LoadingProvider.jsx

    import React, { useState } from 'react';
    import PropTypes from 'prop-types';
    import { LoadingContext } from '../../contexts/loading';
    
    export function LoadingProvider(props) {
      const [loading, setLoading] = useState(false);
    
      return (
        <LoadingContext.Provider
          value={{
            loading: loading,
            show: () => setLoading(true),
            hide: () => setLoading(false)
          }}>
          {props.children}
        </LoadingContext.Provider>
      );
    }
    
    LoadingProvider.propTypes = {
      children: PropTypes.node
    };
    

    Như các bạn nhìn thấy trong đoạn mã trên chúng ta đã sử dụng LoadingContext truyền trang thái loading và các hàm showhide xuống các component con thông qua React Context.

    Tại App component bạn đặt LoadingProvider là component cao nhất để tất các các component con bên trong có thể gọi các hàm showhide khi cần thiết.

    App.js

    import React from 'react';
    import { LoadingProvider } from './providers/LoadingProvider';
    import { useLoading } from './context/loading.js';
    import logo from './logo.svg';
    
    import './App.css';
    
    function App() {
      const { show, hide } = useLoading();
      return (
        <LoadingProvider>
          <div className="App">
            <header className="App-header">
              <img src={logo} className="App-logo" alt="logo" />
              <p>
                Edit <code>src/App.js</code> and save to reload.
              </p>
              <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer">
                Learn React
              </a>
            </header>
          </div>
        </LoadingProvider>
      );
    }
    
    export default App;
    

    Tại component bạn muốn gọi show hay hide loading thì chỉ cần thực hiện như sau:

    • import hàm useLoading() được khai báo trong context/loading.js
    import { useLoading } from './context/loading.js';
    
    • Trong component bạn sử dụng hook useLoading để lấy các hàm showhide:
    const { show, hide } = useLoading();
    

    Giờ chúng ta sửa lại một chút ở LoadingProvider để có thể show loading indicator

    import React, { useState } from 'react';
    import PropTypes from 'prop-types';
    import { LoadingContext } from '../../contexts/loading';
    
    function Loading() {
      return <div>Loading...</div>;
    }
    
    export function LoadingProvider(props) {
      const [loading, setLoading] = useState(false);
    
      return (
        <LoadingContext.Provider
          value={{
            loading: loading,
            show: () => setLoading(true),
            hide: () => setLoading(false)
          }}>
          <>
            {loading && <Loading />}
            {props.children}
          </>
        </LoadingContext.Provider>
      );
    }
    
    LoadingProvider.propTypes = {
      children: PropTypes.node
    };
    

    Các bạn để ý component Loading nhé. Chúng ta chỉ cần thêm một chút kỹ thuật css để cho component này đề lên trên tất cả các component khác bằng z-index đã có được loading indicator có thể được gọi ở bất kỳ đâu rồi.

    Cám ơn các bạn đã theo dõi bài viết. Hy vọng bài viết có thể giúp ích cho dự án của các bạn.

  • Hướng dẫn sử dung mono repository với yarn workspaces

    Hướng dẫn sử dung mono repository với yarn workspaces

    Thông thường khi viết ứng dụng node, chúng ta thương sử dụng các thử viện có sẵn trên npmjs. Trong bài viết này tôi sẽ hướng dẫn các bạn tạo một dự án node có thể chia nhỏ thành các packages nhưng vẫn có thể viết trên cùng một repository. Yarn cung cấp cho bạn chức năng để tạo và link các packages với nhau bằng yarn workspaces. Chúng ta tìm hiểu yarn workspaces hoạt động ra sao nhé

    Các bạn có thể tham khảo hướng dẫn cài đặt yarn ở đây

    Tạo mới project

    hieunv@HieuNV hieunv % mkdir mono
    hieunv@HieuNV hieunv % cd mono
    hieunv@HieuNV mono % yarn init
    yarn init v1.22.0
    question name (mono):
    question version (1.0.0):
    question description:
    question entry point (index.js):
    question repository url:
    question author:
    question license (MIT):
    question private:
    success Saved package.json
    ✨  Done in 5.46s.
    hieunv@HieuNV mono %
    

    Enable yarn workspaces

    • Thêm đoạn "workspaces": ["packages/*"] vào package.json

    package.json

    {
      "name": "mono",
      "workspaces": ["packages/*"],
      "version": "1.0.0",
      "main": "index.js",
      "license": "MIT"
    }
    

    Tiến hành tạo mono repository theo các bước sau:

    • Tạo 3 package trong thư mục packages như sau:
    hieunv@HieuNV mono % find . -print | sed -e 's;[^/]*/;|____;g;s;____|; |;g'
    .
    |____package.json
    |____packages
    | |____package-c
    | | |____index.js
    | | |____package.json
    | |____package-b
    | | |____index.js
    | | |____package.json
    | |____package-a
    | | |____index.js
    | | |____package.json
    
    • Tại thư mục package-apackage-b, thực hiện đăng ký package:

    package-a

    hieunv@HieuNV package-a % yarn link
    yarn link v1.22.0
    success Registered "package-a".
    info You can now run `yarn link "package-a"` in the projects where you want to use this package and it will be used instead.
    ✨  Done in 0.04s.
    

    package-b

    hieunv@HieuNV package-b % yarn link
    yarn link v1.22.0
    success Registered "package-b".
    info You can now run `yarn link "package-b"` in the projects where you want to use this package and it will be used instead.
    ✨  Done in 0.04s.
    
    • Thêm package-apackage-b như là dependency của package-c:
    hieunv@HieuNV package-c % yarn link "package-a"
    yarn link v1.22.0
    success Using linked package for "package-a".
    ✨  Done in 0.04s.
    hieunv@HieuNV package-c % yarn link "package-b"
    yarn link v1.22.0
    success Using linked package for "package-b".
    ✨  Done in 0.04s.
    
    • Trong package-a tạo các file sau:

    package-a/package.json

    {
      "type": "module",
      "name": "package-a",
      "version": "1.0.0",
      "main": "index.js",
      "license": "MIT"
    }
    

    package-a/index.js

    export function packageA() {
      console.log('Package A');
    }
    
    • Trong của package-b tạo các file sau:

    package-b/package.json

    {
      "type": "module",
      "name": "package-b",
      "version": "1.0.0",
      "main": "index.js",
      "license": "MIT"
    }
    

    package-b/index.js

    export function packageB() {
      console.log('Package B');
    }
    
    • Sử dụng các hàm được khai báo package-apackage-b

    package-c/package.json

    {
      "type": "module",
      "name": "package-c",
      "version": "1.0.0",
      "main": "index.js",
      "license": "MIT"
    }
    

    package-c/index.js

    import { packageA } from 'package-a';
    import { packageB } from 'package-b';
    
    packageA();
    packageB();
    
    • Run package-c/index.js
    hieunv@HieuNV package-c % node index.js
    (node:10165) ExperimentalWarning: The ESM module loader is experimental.
    Package A
    Package B
    

    Tài liệu tham khảo:

    • https://classic.yarnpkg.com/en/docs/workspaces/
  • Hướng dẫn cài đặt DynamoDB với Docker

    Hướng dẫn cài đặt DynamoDB với Docker

    Cài đặt docker

    • Cập nhập brew
    hieunv@HieuNV ~ % brew update && brew upgrade
    Updated 1 tap (homebrew/core).
    No changes to formulae.
    
    • Cài đặt docker
    brew install docker
    
    • Kiểm tra docker sau khi cài đặt
    hieunv@HieuNV ~ % docker -v
    Docker version 19.03.1, build 74b1e89
    
    • Khởi động Docker
    Kiểm tra trạng thái docker

    Tạo DynamoDB container

    • Tạo docker-compose.yml để up DynamoDB

    docker-compose.yml

    version: '3'
    services:
      dynamodb:
        image: amazon/dynamodb-local
        container_name: dynamodb
        ports:
          - '8000:8000'
        volumes:
          - ./dynamodb/data:/home/dynamodblocal/data
        entrypoint: java
        command: '-jar DynamoDBLocal.jar -sharedDb -dbPath /home/dynamodblocal/data'
    
    • Up DynamoDB container sau khi tạo docker-compose.yml
    docker-compose up -d
    
    Kiểm tra trạng thái docker sau khi up DynamoDB container

    Bạn có thể truy cập vào docker shell bằng link sau:

    http://localhost:8000/shell
    

    Như vậy là bạn đã tạo xong container cho DynamoDB rồi.

  • Quản lý python packages như thế nào cho đúng

    Quản lý python packages như thế nào cho đúng

    Maven dùng pom để quản lý packages, Node thì có packages.json. Anh em python thì quản lý python packages bằng pip (Package installer for Python). Tuy nhiên khi sử dụng pip sẽ gặp phải tình huống các dự án khác nhau sử dụng dánh sách packages khác nhau. Vấn đề lớn hơn nữa có thể xảy ra tình huống 2 dự án nào đó sử dụng cùng package ở hai phiên bản khách nhau. Trong bài viết này tôi sẽ hướng dẫn các bạn cách quản lý python packages cho các dự án khác nhau mà không bị phụ thuộc vào nhau. Chúng ta cùng bắt đầu nhé.

    Kiểm tra tình trạng hoạt động của brew nào:

    hieunv@HieuNV ~ % brew update && brew upgrade
    Updated 1 tap (homebrew/core).
    ==> Updated Formulae
    abcm2ps             consul              grails              pjproject
    allure              dps8m               graphicsmagick      rancid
    appstream-glib      erlang              i2p                 ratfor
    byteman             flyway              jruby               stress-ng
    camlp5              folly               libgphoto2          zydis
    cargo-instruments   fonttools           libjwt
    cfengine            gmsh                mill
    clblast             gptfdisk            mongoose
    

    Cài đặt python 3

    brew install python
    

    Mặc định MacOS sử dụng python 2

    hieunv@HieuNV ~ % python -V
    Python 2.7.16
    hieunv@HieuNV ~ % python3 -V
    Python 3.7.0
    

    Các bạn cần thêm đoạn sau vào ~/.zshrc.

    export PATH="/usr/local/opt/python/libexec/bin:/usr/local/sbin:$PATH"
    

    Nếu đang mở Terminal bạn cần đóng Terminal lại rồi kiểm tra lại python version

    hieunv@HieuNV ~ % python -V
    Python 3.7.6
    hieunv@HieuNV ~ % pip -V
    pip 19.3.1 from /usr/local/lib/python3.7/site-packages/pip (python 3.7)
    

    Cài đặt virtualenvvirtualenvwrapper

    Với python các packages không được cài đặt cục bộ giống như node. Do đó chúng ta cần tạo ra các môi trường khác nhau với các packages khác nhau để sử dụng cho các dự án khác nhau.

    Cài đặt virtualenv

    pip install virtualenv
    

    Cài đặt virtualenvwrapper

    pip install virtualenvwrapper
    

    Activate virtualenv mỗi khi bật khởi động Terminal

    Các bạn thêm đoạn sau vào ~/.zshrc để virutalenv có thể dượcd khởi động mỗi khi bạn bật Terminal

    export WORKON_HOME=$HOME/.virtualenvs
    source /usr/local/bin/virtualenvwrapper.sh
    

    Tiến hành tạo môi trưởng ảo và cài đặt packages mong muốn

    • Tạo môi trường ảo
    hieunv@HieuNV ~ % mkvirtualenv a
    created virtual environment CPython3.7.6.final.0-64 in 515ms
      creator CPython3Posix(dest=/Users/hieunv/.virtualenvs/a, clear=False, global=False)
      seeder FromAppData(download=False, pip=latest, setuptools=latest, wheel=latest, via=copy, app_data_dir=/Users/hieunv/Library/Application Support/virtualenv/seed-app-data/v1)
      activators BashActivator,CShellActivator,FishActivator,PowerShellActivator,PythonActivator,XonshActivator
    virtualenvwrapper.user_scripts creating /Users/hieunv/.virtualenvs/a/bin/predeactivate
    virtualenvwrapper.user_scripts creating /Users/hieunv/.virtualenvs/a/bin/postdeactivate
    virtualenvwrapper.user_scripts creating /Users/hieunv/.virtualenvs/a/bin/preactivate
    virtualenvwrapper.user_scripts creating /Users/hieunv/.virtualenvs/a/bin/postactivate
    virtualenvwrapper.user_scripts creating /Users/hieunv/.virtualenvs/a/bin/get_env_details
    (a) hieunv@HieuNV ~ %
    

    Các bạn để ý dòng cuối cùng (a). Sau khi tạo xong thì zsh đã activate vào môi trường ảo.

    • Active vào môi trường ảo nếu môi trường chưa được active thì có thể làm như sau:
    hieunv@HieuNV ~ % workon a
    (a) hieunv@HieuNV ~ %
    
    • Khi muốn thoát khỏi môi trường ảo thì có thể làm như sau:
    (a) hieunv@HieuNV ~ % deactivate
    hieunv@HieuNV ~ %
    
    • Để cài đặt packages thì tiến hành cài đặt bằng pip như bình thường
    pip install boto3
    

    Sử dụng môi trường ảo với python 2

    • Cài đặt python 2
    brew install python2
    
    • Tạo môi trường ảo sử dụng python bằng tham số -p
    mkvirtualenv py2 -p python2
    

    Export package list để install trên máy khác

    (a) hieunv@HieuNV ~ % pip freeze > requirements.txt
    (a) hieunv@HieuNV ~ % cat requirements.txt
    boto3==1.12.11
    botocore==1.15.11
    docutils==0.15.2
    jmespath==0.9.5
    python-dateutil==2.8.1
    s3transfer==0.3.3
    six==1.14.0
    urllib3==1.25.8
    

    Cài đặt packages sử dụng requirements.txt

    pip install -r requirements.txt
    

    Tài liệu tham khảo:

    • https://swapps.com/blog/how-to-configure-virtualenvwrapper-with-python3-in-osx-mojave/
  • Hướng dẫn cài đặt yarn trên MacOS

    Hướng dẫn cài đặt yarn trên MacOS

    Trong bài viết này tôi sẽ hướng dẫn các bạn cài đặt và sử dụng yarn để quản lý node packages thay cho npm

    Cài đặt brew

    /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
    
    brew update && brew upgrade
    

    Cài đặt yarn

    brew install yarn
    

    Kiểm tra phiên bản yarn

    hieunv@HieuNV ~ % yarn -v
    1.22.0
    

    Tạo mới node project bằng yarn

    yarn init
    

    Thêm dependencies

    yarn add react
    

    Thêm devDependencies

    yarn add node-sass -D
    

    Xoá dependencies hoặc devDependencies

    yarn remove react