自製日期選擇器-2 (custom hook)
延續 上一集
確定元件的輸入輸出介面後
(回顧四個基本屬性: isVisible
, displayDate
, mode
, onConfirm
)
現在要開始實作功能啦
我們可以把實作步驟切分成幾個部分:
- 排版: 如何動態顯示每個月的日期數以及星期
- 單日日期操作: 每次點選都會更新
data.date
- 多日日期操作: 有時點選是設定開始日
date.startDate
,有時是結束日endDate
,需設計操控流程 - 效能問題: 目前為止每次當元件內有一堆按鈕的時候都會有不可忽略的卡頓(因為每次點擊都會讓每個按鈕重新渲染),希望我能研究出如何減少這個卡頓時間
今天這章節就來看看如何做日期的動態顯示吧!
動態顯示日期
首先我們來想一想
一般的日曆app都怎麼排版?
每個月第一天的星期都不一樣
並且前後分別會補上個月最後幾天和下個月前幾天
直到補滿整行,像這樣(網路找的圖):
要達到這個目標,必須先有三個資訊:
- 本月有幾天?
days
- 本月第一天是星期幾?
firstDay
- 上個月有幾天?
prevMonthDays
所以我打算做一個useDaysOfMonth
custom hook 來提供這些訊息
- Custom Hook 是一組包裝起來的邏輯,有自己的 life cycle,你可以在其中使用
useState
、useEffect
之類的 React hook。如果你還記得,hook 不是有很多使用上的限制嗎? 例如只能存在於元件內的第一層而不能被包在元件內的函數內,這項限制讓你沒辦法把重複用的邏輯包起來。而 custom hook 就是為此而生。
Custom Hook: useDaysOfMonth
custom hook 有個條件,就是一定要是use
開頭,如此一來 React 運行時才能判斷元件內的 hook 是不是都在最上層,所以別忘了這步。
除此要求之外,一個 custom hook 看起來就像是不做渲染的 React 元件,或是可以放 hook 的一般函數。
那就開始吧,首先我們建立一個檔案叫 useDaysOfMonth.jsx
1 | // in useDaysOfMonth.jsx |
接下來我們需要拆解輸入進來的日期物件,得到年份,月份
1 | const year = inputTime.getFullYear() |
然後我們就要依照年月來獲得 days, firstDay, prevMonthDays 三個資訊
*注意不要用日期物件的setDate
來做這些事,因為Date()
的 method 都會 mutate(會改變物件自身的值,而不會另外建立一個物件)
1 | setDays( new Date(year,month+1,0).getDate() ) |
過程很簡單,都是基本的Date
物件取值
再來我們希望在任何使用這個 hook 的元件中,每當 input 值改變時,輸出值也要跟著更新
這不就是useEffect
的功能嗎!
所以我們將以上運算放進useEffect
中,並將inputTime
設為 dependency
1 | useEffect(()=>{ |
完成! 完整的code如下:
1 | import { useState, useEffect } from 'react' |
一個小細節,我把輸出的參數用大括號(object literal)包起來,這樣在存取參數時就不需考慮順序
什麼意思呢? 看以下的使用方式就知道了
1 | const {firstDay, prevMonthDays, days} = useDaysOfMonth(new Date()) |
可以看到我取得三個參數的順序不同,但仍然會work
缺點是這樣就一定要把參數名稱打對了,想要更改參數名就要打{firstDay: xxx}
反之,如果你回傳的是用中括號(array literal)包起來[firstDay, prevMonthDays, days]
取得參數時的順序就很重要了,不過就可以直接改名