我正在尝试学习和试验 React 钩子。对于一个小型测试项目,我想在用户更改显示模式或更改日期时更新日历网格。
这是Calendar.jsx
.状态存储在此处。date
和setDate
作为道具传递给CalendarDatePicker
。
import React, {useState} from 'react';
import PropTypes from 'prop-types';
import CalendarDisplayMode from './CalendarDisplayMode';
import CalendarDatePicker from './CalendarDatePicker';
import CalendarGrid from './CalendarGrid';
const Calendar = props => {
const [date, setDate] = useState(props.date);
const [mode, setMode] = useState(props.mode);
return (
<div className="bg-light border border-dark rounded p-1">
<CalendarDisplayMode mode={mode} setMode={setMode} />
<CalendarDatePicker date={date} setDate={setDate} />
<CalendarGrid displayDate={date} displayMode={mode} />
</div>
);
}
Calendar.propTypes = {
date: PropTypes.instanceOf(Date).isRequired,
mode: PropTypes.oneOf(['day', 'week', 'month', 'year']).isRequired
}
export default Calendar;
这是CalendarDatePicker.jsx
.当任何输入更改时setDate
将被调用并传递新日期。这应该更新Calendar
中的状态。
import React from 'react';
import PropTypes from 'prop-types';
const CalendarDatePicker = ({date, setDate}) => {
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
const handleYearChange = event => setDate(new Date(event.target.value, month, day));
const handleMonthChange = event => setDate(new Date(year, event.target.value - 1, day));
const handleDayChange = event => setDate(new Date(year, month, event.target.value));
return (
<div>
<div className="form-group">
<label htmlFor="display-year">Year</label>
<input
type="number"
id="display-year"
name="display-year"
className="form-control form-control-sm"
min="1"
value={year}
onChange={handleYearChange} />
</div>
<div className="form-group">
<label htmlFor="display-month">Month</label>
<input
type="number"
id="display-month"
name="display-month"
className="form-control form-control-sm"
min="1"
max="12"
value={month}
onChange={handleMonthChange} />
</div>
<div className="form-group">
<label htmlFor="display-day">Day</label>
<input
type="number"
id="display-day"
name="display-day"
className="form-control form-control-sm"
min="1"
max="31"
value={day}
onChange={handleDayChange} />
</div>
</div>
);
}
CalendarDatePicker.propTypes = {
date: PropTypes.instanceOf(Date).isRequired,
setDate: PropTypes.func.isRequired
}
export default CalendarDatePicker;
但我观察到奇怪的行为。如果我递增或递减年份字段中的数字,则月份字段将增加其值。如果我递增或递减月份字段,则其他字段都不会更改值(如预期的那样(。如果我递增或递减日字段中的数字,则月份字段将递增其值。如果我将日字段中的数字递增或递减到足够多,则月份字段将滚动(从 12 到 1(,并且年份字段也会增加其值。
谁能解释一下这里可能发生的事情以及如何解决它?
const handleYearChange = event => setDate(new Date(event.target.value, month, day));
const handleMonthChange = event => setDate(new Date(year, event.target.value - 1, day));
const handleDayChange = event => setDate(new Date(year, month, event.target.value));
在handleYearChange
和handleDayChange
你正在通过month
这实际上是date.getMonth() + 1
- 你需要做month - 1
.
这就是为什么当年份或日期发生变化时,您的月份会增加 1。
编辑: 以这种方式处理它,您还可以更改一个月中天数等的总数,而无需将其硬编码为 31。只是此设计如何让您变得灵活的一个例子。
我个人不明白你为什么要setDate
传递给子组件。.我看不出这有什么意义,也不明白你从中获得什么。
在我看来,问题源于你如何处理你的设计。
每次出现问题时,const year
、const day
、const month
都再次运行......这是你应该使用state
的地方。只需将date
作为道具传递,然后处理DatePicker
组件本地对该日期的所有更改......让孩子直接接触父母并改变它的状态对我来说感觉很奇怪。
像这样的事情就是我会如何处理这个问题 - 感觉更干净。
const { useState, useEffect } = React;
const { render } = ReactDOM;
const CalendarDatePicker = ({date, onDateChange}) => {
const [year, setYear] = useState();
const [month, setMonth] = useState();
const [day, setDay] = useState();
const [daysInMonth, setDaysInMonth] = useState();
useEffect(() => {
setYear(date.getFullYear());
setMonth(date.getMonth() + 1);
setDay(date.getDate());
}, [date])
useEffect(() => {
let dim = getDaysInMonth(month, year);
setDaysInMonth(dim);
}, [month, year]);
const handleYearChange = event => {
let nd = new Date(event.target.value, month, day);
setYear(nd.getFullYear());
onDateChange(nd);
}
const handleMonthChange = event => {
let nd = new Date(year, event.target.value - 1, day)
setMonth(nd.getMonth() + 1);
onDateChange(nd);
}
const handleDayChange = event => {
let nd = new Date(year, month, event.target.value);
setDay(nd.getDate());
onDateChange(nd);
}
function getDaysInMonth(month, year) {
return new Date(year, month, 0).getDate();
}
return (
<div>
<div className="form-group">
<label htmlFor="display-year">Year</label>
<input
type="number"
id="display-year"
name="display-year"
className="form-control form-control-sm"
min="1"
value={year}
onChange={handleYearChange} />
</div>
<div className="form-group">
<label htmlFor="display-month">Month</label>
<input
type="number"
id="display-month"
name="display-month"
className="form-control form-control-sm"
min="1"
max="12"
value={month}
onChange={handleMonthChange} />
</div>
<div className="form-group">
<label htmlFor="display-day">Day</label>
<input
type="number"
id="display-day"
name="display-day"
className="form-control form-control-sm"
min="1"
max={daysInMonth}
value={day}
onChange={handleDayChange} />
</div>
</div>
);
}
const Calendar = props => {
const handleDateChange = event => {
console.log('You can change the `Calendar` during this event when the date changes...', event);
}
return (
<div className="bg-light border border-dark rounded p-1">
<CalendarDatePicker date={props.date} onDateChange={handleDateChange} />
</div>
);
}
render(<Calendar date={new Date(Date.now())} />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.9.0/umd/react-dom.production.min.js"></script>