「react datepicker」でググると、「react-datepicker」のライブラリが出てくると思います。
react-datepickerを使う機会があったので、備忘録にreact-datepickerの機能についてご紹介しようと思います。
0. 事前準備
(create-react-appでサンプルを作成しております。)
早速ライブラリをインストールしましょう!
npm install react-datepicker --save
Datepickerを使うコンポーネントにimportさせます。
import DatePicker from "react-datepicker"
import "react-datepicker/dist/react-datepicker.css"
シンプルなDatepickerの書き方はこうです。
const SimpleDatePicker = () => {
const initialDate = new Date()
const [startDate, setStartDate] = useState(initialDate)
const handleChange = (date) => {
setStartDate(date)
}
return (
<DatePicker
selected={startDate}
onChange={handleChange}
/>
)
}
以下の例では、日付の処理をする関数を作って呼び出すようにしています。
/**
* JST基準に変換して返す
* @param {string} dateTimeStr YYYY-MM-DDTHH:mm:00Z
* @returns {moment.Moment}
*/
const parseAsMoment = (dateTimeStr) => {
return moment.utc(dateTimeStr, 'YYYY-MM-DDTHH:mm:00Z', 'ja').utcOffset(9)
}
/**
* 日付形式に変換して返す
* @param {moment.Moment} momentInstance
* @returns {string}
*/
const toUtcIso8601str = (momentInstance) => {
return momentInstance
.clone()
.utc()
.format('YYYY-MM-DDTHH:mm:00Z')
}
1. ある要素をクリックしたらカレンダーを表示させたい場合
Custom inputにDatePickerを表示させるトリガー要素を指定できます。
下の例ではbutton
にクリックされた日付を表示するようにしています。ボタンにはクリックイベントなど特に指定いりません!
const CustomInputDatePicker = () => {
const [startDate, setStartDate] = useState(toUtcIso8601str(moment()))
const handleChange = (selectedDate) => {
setStartDate(toUtcIso8601str(moment(selectedDate)))
}
return (
<DatePicker
selected={moment(startDate).toDate()}
onChange={handleChange}
customInput={
<button>
{parseAsMoment(startDate).format('YYYY/MM/DD')}
</button>
}
/>
)
}
2. カレンダーのヘッダーを変更したい場合
Custom headerを使うと、カレンダー上の年月を変更できるセレクトボックスと、次月前月への矢印ボタンを自分でカスタマイズすることができます。
... // 省略
import getMonth from 'date-fns/getMonth'
import getYear from 'date-fns/getYear'
import _ from 'lodash'
...
const years = _.range(2000, getYear(new Date()) + 1, 1)
const months = Array.from(Array(12).keys())
...
const CustomHeaderDatePicker = () => {
const [startDate, setStartDate] = useState(toUtcIso8601str(moment()))
const handleChange = (selectedDate) => {
setStartDate(toUtcIso8601str(moment(selectedDate)))
}
return (
<DatePicker
selected={moment(startDate).toDate()}
onChange={handleChange}
renderCustomHeader={({
date,
changeYear,
changeMonth,
decreaseMonth,
increaseMonth,
prevMonthButtonDisabled,
nextMonthButtonDisabled
}) => (
<div>
<button
onClick={decreaseMonth}
disabled={prevMonthButtonDisabled}
>
前月へ移動
</button>
<select
value={getYear(date)}
onChange={({ target: { value } }) => changeYear(value)}
>
{years.map((option) => (
<option key={option} value={option}>
{option}年
</option>
))}
</select>
<select
value={getMonth(date)}
onChange={({ target: { value } }) => changeMonth(value)}
>
{months.map((option) => (
<option key={option} value={option}>
{option + 1}月
</option>
))}
</select>
<button
onClick={increaseMonth}
disabled={nextMonthButtonDisabled}
>
次月へ移動
</button>
</div>
)}
/>
)
}
3. カレンダーをラップする時の小ネタ
Calerndar container内に、カレンダーをラップする要素を指定できます。
見出しやテキスト、他のイベントを設置したい時に使えそうです。
下の例ではcalendarContainer
にラップする<DatetimePickerWrapper />
コンポーネントを指定しています。children
にカレンダーが入ります。
const ContainerDatePicker = () => {
const [startDate, setStartDate] = useState(toUtcIso8601str(moment()))
const handleChange = (selectedDate) => {
setStartDate(toUtcIso8601str(moment(selectedDate)))
}
return (
<DatePicker
selected={moment(startDate).toDate()}
onChange={handleChange}
calendarContainer={DatetimePickerWrapper}
/>
)
}
const DatetimePickerWrapper = ({ children }) => {
return (
<div style={{
overflow: 'hidden',
padding: 30,
backgroundColor: 'skyblue',
boxShadow: '6px 6px #668AD8',
}}>
{children}
</div>
)
}
4. カレンダーの範囲を指定する
公式サイトのDemoにあるDate Rangeを使うと、日付の範囲を指定できます。
DatePickerを2つ置いて、開始日時を選択する方はselectsStart
、終了日時を選択する方はselectsEnd
を指定します。
また、それぞれ日付を選択するchangeイベントを用意し、選択された日にちのselected
のvalueも開始・終了のステートを指定します。
下のサンプルでは開始日を今日より1週間前、終了日を今日にしています。
const DateRangeDatePicker = () => {
const [startDate, setStartDate] = useState(toUtcIso8601str(moment().subtract(7, 'days')))
const [endDate, setEndDate] = useState(toUtcIso8601str(moment()))
const handleChangeStart = (selectedDate) => {
setStartDate(toUtcIso8601str(moment(selectedDate)))
}
const handleChangeEnd = (selectedDate) => {
setEndDate(toUtcIso8601str(moment(selectedDate)))
}
return (
<Fragment>
<DatePicker
selected={moment(startDate).toDate()}
selectsStart
startDate={moment(startDate).toDate()}
endDate={moment(endDate).toDate()}
onChange={handleChangeStart}
/>
<DatePicker
selected={moment(endDate).toDate()}
selectsEnd
startDate={moment(startDate).toDate()}
endDate={moment(endDate).toDate()}
onChange={handleChangeEnd}
/>
</Fragment>
)
}
Inlineを指定するとカレンダーがベタ表示されるようになるので、使い勝手がよくなりそうです!
5. カレンダーを日本語対応させる
Localで"ja"
を指定すると、日本語になります。
... // 省略
import DatePicker, { registerLocale } from 'react-datepicker'
import ja from 'date-fns/locale/ja'
registerLocale('ja', ja)
...
const LocalDatePicker = () => {
const [startDate, setStartDate] = useState(toUtcIso8601str(moment()))
const handleChange = (selectedDate) => {
setStartDate(toUtcIso8601str(moment(selectedDate)))
}
return (
<Fragment>
<DatePicker
locale="ja"
selected={moment(startDate).toDate()}
onChange={handleChange}
/>
</Fragment>
)
}
6. カレンダーの複数表示
カレンダーを2ヶ月続けて横並び表示させる場合はMultiple monthsを使います。
下の例では「今月」「先月」をクリックした時、開始・終了日をsetState
してハイライトさせています。
※注意:下の例はChangeイベント系を除いて書いています!monthsShown
、inline
等の使い方だけご参考ください。
const MultipleDatePicker = () => {
const [startDate, setStartDate] = useState(toUtcIso8601str(moment().subtract(7, 'days')))
const [endDate, setEndDate] = useState(toUtcIso8601str(moment()))
... // 省略
return (
<div>
<p>開始日: {parseAsMoment(startDate).format('YYYY/MM/DD')}</p>
<p>終了日: {parseAsMoment(endDate).format('YYYY/MM/DD')}</p>
<DatePicker
inline
selected={moment(startDate).toDate()}
startDate={moment(startDate).toDate()}
endDate={moment(endDate).toDate()}
onChange={handleChange}
monthsShown={2}
calendarContainer={({ children }) => (
<div>
<div style={{ marginBottom: 20 }}>
<button onClick={() => handleChangeDateRange('thisMonth')}>今月</button>
<button onClick={() => handleChangeDateRange('lastMonth')}>先月</button>
</div>
{children}
</div>
)}
/>
</div>
)
}
終わりに
「こんなオプションも用意してくれてるのか!」と感動したので、備忘録として残しました笑。
上記で紹介した指定をした上でスタイルを整えてあげると、下のような感じで良い感じのカレンダーが出来上がります!