npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

minh-custom-hooks-release

v1.0.13

Published

Đây là custom hooks được viết bởi hoanggminh2702, bao gồm:

Downloads

122

Readme

Minh Custom Hooks

Đây là custom hooks được viết bởi hoanggminh2702, bao gồm:

  1. useToggle
  2. useDebounce group
  3. useMount
  4. useDidUpdate
  5. ~~useEventEffect~~
  6. ~~useEventDidUpdate~~
  7. ~~useEventMemo~~

⚠️ Cảnh báo: Các hook Event bị loại bỏ do React đã support chúng native


Ví dụ Sau đây là ví dụ về xử lý loading trong ReactJs bằng cách truyền thông và bằng useToggle

Truyền thống

...
// Loading state
const [isLoading, setIsLoading] = useState(false)

// Handle 1 tác vụ bất đồng bộ khi click btn
const handleCallApi = () => {
    setLoadingState(true)
    apiCall().then(res => {
        setLoadingState(false)
        ...
    })
}

return (
    <div>
        <button onClick={handleCallApi}>Call api</button>
    </div>
)

Có thể thấy trong trường hợp chỉ sử dụng state để handleLoading, việc đọc code trở không được trực quan, khi hầu hết việc thay đổi trạng thái loading về mặt thị giác đều là setState, gây cản trở trong quá trình đọc hiểu code thay vào đó, ta có thể sử dụng useToggle

// Loading state và các handler của nó được defined sẵn
const {state: isLoading, on: startLoading, off: stopLoading} = useToggle(false)

// Handle 1 tác vụ bất đồng bộ khi click btn
const handleCallApi = function() {
    // Khi nhìn vào mặc nhiên sẽ biết được ngay đây là hành động bắt đầu loading
    startLoading()
    apiCall().then(res => {
        // Tương tự với startLoading
        stopLoading()
        ...
    })
}

...

  • Hook này được viết ra để xử lý các tác vụ cần tới kỹ thuật debounce.

  • Vậy kỹ thuật debounce là gì? Kỹ thuật debounce trong ReactJs nói riêng là kỹ thuật trì hoãn việc update state khi thực hiện setState liên tục trong 1 khoảng thời gian ngắn, việc update lại state chỉ được thực hiện sau khi hành động setState dừng lại sau 1 khoảng thời gian được quy ước từ trước. Việc này có thể giảm thiểu số lần re-render của app. Việc sử dụng debounce trong 1 số trường hợp logic code được viết để gọi API mỗi khi state được thay đổi còn giúp giảm gánh nặng cho server, tránh gửi quá nhiều request không cần thiết tới server.

  • Sau đây là 1 ví dụ đơn giản về ý nghĩa của việc áp dụng kỹ thuật debounce

Khi không sử dụng kỹ thuật debounce

...
const [searchText, setSearchText] = useState<string>("")
const [listData, setListData] = useState<TData>([])

// Thực hiện call api tìm kiếm khi searchText thay đổi
useEffect(() => {
    /**
     * callback được gọi liên tục khi searchText thay đổi, dẫn tới
     * api được call liên tục, trong trường hợp mạng ổn định có thể dẫn
     * tới trường hợp request gửi trước nhưng kết quả trả về sau, dẫn
     * tới việc listData được set là kết quả request cũ
    */
    searchApi(searchText).then(res => setListData(res))
}, [searchText])

return (
    <div>
        <input value={searchText} onChange={e =>
            setSearchText(e.target.value)
        } />

        {listData.map(data => ...) }
    </div>
)

Khi sử dụng kỹ thuật debounce Ví dụ này sẽ sử dụng hook useDebounceState

...
const {
    // Đây là state thực tế chưa áp dụng kỹ thuật debounce
    actualState: searchText,
    debouncedState: debouncedText,
    setState: setSearchText
} = useDebounceState<string>("",
    // Thời gian sau khi hành động setState dừng lại, đơn vị ms
    500
)

// Thực hiện call api tìm kiếm khi searchText thay đổi
useEffect(() => {
    /**
     * callback được gọi chỉ khi debouncedText thay đổi, khi  việc
     * setSearchText dừng lại 500ms
    */
    searchApi(searchText).then(res => setListData(res))
}, [searchText])

...

Có 3 debounce hook được expose:

Input:

  1. defaultState: State mặc định
  2. debounceTime: Thời gian debounce, mặc định 500ms

Output:

  1. debouncedState: state đã được debounce, chỉ thay đổi khi hành động > setState được dừng lại sau debounceTime
  2. stop: dừng việc set lại debouncedState nếu nó chưa được thay đổi
  3. status: Gồm 2 trạng thái:
  • DONE: Trạng thái sau khi hoàn thành việc debounce, debouncedValue được thay đổi
  • PENDING: Trạng thái khi đang trong quá trình debounce

Ví dụ:

...
const [searchText, setSearchText] = useState("")
const {
    debouncedState: debouncedText,
    status
    // Khi searchText thay đổi, debounce sẽ được chạy, khi thực hiện xong
    // thì debouncedState được update lại
} = useDebounce<string>(searchText,
    500
)

useEffect(() => {
    searchApi(debouncedText).then(res => setListData(res))
}, [debouncedText])

return (
    <div>
        <input value={searchText} onChange={e =>
            setSearchText(e.target.value)
        } />

        {
            /**
             *  Nếu không sử dụng status để xử lý loading, mà chỉ loading khi
             * gửi call API thì sau khi quá trình debounce được thực hiện thì
             * API mới được gọi, bắt đầu quá trình loading, giao diện bị giật,
             * Không thân thiện với người dùng, thay vào đó, ngay khi bắt đầu
             * quá trình debounce, ngay lập tức loading
             *
             * */
            status === DebounceValueStatus.PENDING ? {"Loading"} : listData.map(data => ...)
        }
    </div>
)
...

Lưu ý: DebounceValueStatus là Enum được defined built-in khi thư viện được viết, bạn có thế import nó trong quá trình sử dụng như sau.

import { DebounceValueStatus } from "minh-custom-hooks-release/dist/esm/hooks/useDebounce";

...

Input:

  1. defaultState: State mặc định
  2. debounceTime: Thời gian debounce, mặc định 500ms

Output:

  1. debouncedState: như useDebounce
  2. status: useDebounce
  3. actualState: tham số thứ 1 khi sử dụng useState
  4. setState: tham số thứ 2 khi sử dụng useState

Ví dụ

// Thay vì phải viết dài dòng thế này
const [searchText, setSearchText] = useState('')
const { debouncedState: debouncedText, status } = useDebounce<string>(searchText, 500)

// Ta có thể viết ngắn gọn thế này
const {
  actualState: searchText,
  debouncedState: debouncedText,
  setState: setText,
  status,
} = useDebounceState<string>('', 500)

Trong những trường hợp phải debounce 1 tác vụ phức tạp thay vì chỉ thực hiện debounce việc thay đổi state.

Input:

  1. callback: callback cần được debounce, callback này có thể là 1 pure function, hoặc trả về 1 promise.
  2. config: thêm các tùy chọn config
  • debounceTime: tương tự useDebounce debounceTime,
  • Nếu callback là 1 promise: i. onSuccees: callback khi Promise resolve. ii. onError: callback khi Promise reject.
  1. deps: trong trường hợp muốn sử dụng useCallback cho callback.

Output:

  1. returnedData: Data trả về của callback, nếu callback trả về 1 Promise, data này sẽ là kết quả của Promise sau khi thưc thi debounce và Promise resolve kết quả.
  2. status: tương tự useDebounce status.
  3. progressStatus: Toàn bộ trạng thái từ khi debounce đến khi Promise resolve hoặc reject. Các trạng thái tương tự useDebounce status.
  4. run: thực thi callback.
  5. stop: tương tự useDebounce stop

Ví dụ

...
const [fullName, setFullName] = useState<string>("")
const [address, setAddress] = useState<string>("")

const [listData, setListData] = useState([])

const handleSearch = (fullName, address) => {
    searchAPI().then(res => setListData(res))
}

...

Trong trường hợp handleSearch không phải là tác vụ bất đồng bộ, ta có thể debounce nó như sau

...
const {
    run,
    // Vẫn có thể dùng status để xử lý loading
    status
} = useDebounceFn(handleSearch, {
    debounceTime: 500 //ms
});

useEffect(() => {
    // Gọi và sử dụng như bình thường
    run(fullName, address);
}, [])

Khi handleSearch là 1 tác vụ bất đồng bộ, useDebounce cung cấp nhiều cách tiếp cận khác nhau để xử lý debounce và kết quả nhận được

...
const handleSearch = async (fullName, address) => {
    return await searchAPI().then(res => setListData(res))
}
  • Sử dụng useDebounceFn, với returnData:
...

const {run, returnedData} = useDebounceFn(handleSearch)

useEffect(() => {
   run(fullName, address);
}, [])

useEffect(() => {
    setListData(returnedData)
}, [returnedData])

...
  • Sử dụng useDebounceFn, với onSuccess, onError:
...

// Kết hợp với useToggle để xử lý loading
const {state: isLoading, on: startLoading, off: stopLoading } = useToggle(false)

// Chỉ khi callback truyền vào là tác vụ bất đồng bộ thì mới có thể sử dụng onSuccess, onError
const {run, returnedData} = useDebounceFn(handleSearch, {
    onSuccess(res) {
        stopLoading();
        setListData(res);
    }
})

useEffect(() => {
    startLoading();
    run(fullName, address);
}, [])

useEffect(() => {
    setListData(returnedData)
}, [returnedData])

...

Sử dụng để thay thế useEffect với deps rỗng

useEffect(() => {}, [])
// Tương đương với
useMount(() => {})

Ngoài ra, useMount sẽ không bị ảnh hưởng StrictMode, tức là nó chỉ được gọi 1 lần duy nhất khi component được mount, đảm bảo hành vi của app sát với production nhất


Khi useEffect được truyền deps, nó sẽ gọi ngay cả khi vừa được mount cũng như khi deps thay đổi, hook này giúp cho callback chỉ được gọi khi deps thay đổi chứ không gọi khi component vừa được mount. Syntax tương tự như useEffect.


Hook này handle việc callback truyền vào useEffect chỉ được gọi khi deps thay đổi, nhưng vẫn sử dụng được state mới nhất

Ví dụ

const [count, setCount] = useState<number>(0)
const [text, setText] = useState<string>('')

useEffect(() => {
  console.log('count', count)
  console.log('text', text)
}, [count, text])

return (
  <div>
    <button onClick={() => setCount((prev) => prev + 1)}>Increase</button>
    <input onChange={(e) => setText(e.target.value)} />
  </div>
)

Trường hợp này, cho dù ta có thay đổi text bao nhiêu thì khi count thay đổi, kết quả in ra vẫn là:

  • "count", count là kết quả hiện tại của count
  • "text", "" là kết quả ban đầu của text

Nếu ta cố tình thêm text vào list deps, thì khi text thay đổi, callback của effect cũng sẽ được gọi, không đúng với mong muốn ban đầu. Và useEventEffect sẽ giải quyết được vấn đề đó. Syntax tương tự nhiên useEffect.

...
useEventEffect(() => {
    // Chỉ khi count thay đổi thì callback mới được gọi, nhưng text luôn được update giá trị mới nhất
    console.log(count, text)
}, [count, text])

Là sự kết hợp của useDidUpdateuseEventEffect

Đối với useEventEffect, callback được gọi khi deps thay đổi, các state trong deps vẫn được update mới nhất, tương tự với useEventMemo, việc tính toán lại giá trị mới chỉ được thực hiện khi deps thay đổi, tuy nhiên các state khác không nằm trong deps vẫn được cập nhật giá trị mới nhất