Category: Frontend

  • [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.

  • [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 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
    
  • Sử dụng nhiều phiên bản node cùng lúc

    Sử dụng nhiều phiên bản node cùng lúc

    Khi sử dụng node nhiều bạn nghĩ rằng chỉ cần sử dụng phiên bản LTS mới nhất là đủ rồi. Tuy nhiên nếu bạn phải tham gia nhiều dự án cùng lúc (như mình chẳng hạn) thì mới thấy là chẳng dễ gì các dự án sống chung với nhau được. Nói vậy để thấy rằng dùng có thể dùng nhiều phiên bản cùng lúc là cần thiết. Trong bài viết này tôi sẽ hướng dẫn các bản cách sử dụng nhiều phiên bản node cùng lúc.

    nvm là gì?

    nvm là viết tắt của Node Version Manager. Như đã nói ở trên bài viết này sẽ hướng dẫn cách các bạn có thể sử dụng nhiều phiên bản node cùng lúc. Vậy thì nm là cần thiết nhỉ.

    Để cài đặt nvm bạn có thể sử dụng lệnh sau:

    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh | bash
    
    wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh | bash
    

    Tuỳ thuộc vào hệ điều hành bạn đang sử dụng thì cần thêm đoạn sau vào ~/.bash_profile hoặc ~/.profile hoặc ~/.bashrc hoặc ~/.zshrc. Mình đang sử dụng zsh nên mình sẽ thêm vào ~/.zshrc

    export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
    [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
    

    Kiểm tra xem nvm đã được cài thành công chưa

    nvm -v
    

    Giờ thì cài đặt phiên bản node mà muốn sử dụng thôi nào

    Cài đặt phiên bản mới nhất:

    nvm install node
    

    Cài đặt phiên bản xác định mà bạn muốn

    nvm install 10.10.0
    

    Để chuyển qua phiên bản node bản muốn sử dụng

    nvm use v13.9.0
    

    v13.9.0 là tên phiên bản các bạn nhìn thấy sau khi sử dụng nvm ls như ở trên.

    Chi tiết cách sử dụng nvm thì các bạn có thể tham khảo hướng dẫn ở link sau

    https://github.com/nvm-sh/nvm