I am trying to build a calendar app with Full Calendar and Elastic Ui, I use state to manage data/event input, using form to add event to the calendar, and selecting the date on calendar also update the form value. The app is partly working now, I can input events to the calendar, but only after I select/click on the date from the calendar then the form would work fine. However if I input on the text field or date picker in the form before clicking on the calendar, the following error appear and the whole page turn blank.
Maximum update depth exceeded.
The above error occurred in the DatePicker component:
Can't perform a React state update on an unmounted component.
anyone have any idea what's happening? Or how can I find the root of the problem?
There are also two error when the webapp first loaded:
index.js:1 Warning: Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to
this.state
directly or define astate = {};
class property with the desired state in the EuiIcon component. console. @
index.js:1 Warning: Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to
this.state
directly or define astate = {};
class property with the desired state in the CalendarDataProvider component.
import React, { Component, useState } from 'react';
import FullCalendar from '@fullcalendar/react'
import dayGridPlugin from '@fullcalendar/daygrid'
import timeGridPlugin from '@fullcalendar/timegrid'
import interactionPlugin from "@fullcalendar/interaction"
import googleCalendarPlugin from '@fullcalendar/google-calendar'
import moment from 'moment'
import {EuiFlexGroup, EuiFlexItem, EuiFieldText, EuiForm, EuiFormRow, EuiButton, EuiSwitch,
EuiDatePicker, EuiDatePickerRange, EuiRadioGroup
} from '@elastic/eui'
require('dotenv').config()
const AddEventBar = (props) => {
const [isDisabled, setDisabled] = useState(false)
const [StartRepeatDate, setStartRepeatDate] = useState(null)
const [EndRepeatDate, setEndRepeatDate] = useState(null)
const [radioIdSelected, setRadioIdSelected] = useState(null);
const handleChangeStart = (date) => {
setStartRepeatDate(date)
}
const handleChangeEnd = (date) => {
setEndRepeatDate(date)
}
const radioHandler = (optionId) => {
setRadioIdSelected(optionId);
};
const radios = [
{
id: 'daily',
label: 'daily',
},
{
id: 'weekly',
label: 'weekly',
},
];
return (
<EuiFlexGroup>
<EuiFlexItem>
<EuiForm>
<EuiFormRow label='Event Title'>
<EuiFieldText
onChange={props.titleHandler}
aria-label="Use aria labels when no actual label is in use"
/>
</EuiFormRow>
<EuiFormRow label = 'Event Date'>
<EuiDatePicker
placeholder='Start'
selected={moment(props.start_date)}
onChange={props.startDatePickerHandler}
aria-label="Start date"
/>
</EuiFormRow>
<EuiFormRow label='Event Time'>
<EuiDatePickerRange
startDateControl = {
<EuiDatePicker
showTimeSelect
showTimeSelectOnly
selected={moment(props.start_date + ' ' + props.start_time)}
onChange={props.startDatePickerHandler}
timeFormat="hh:mm a"
dateFormat="hh:mm a"
/>}
endDateControl = {
<EuiDatePicker
showTimeSelect
showTimeSelectOnly
selected={moment(props.start_date + ' ' + props.end_time)}
onChange={props.endDatePickerHandler}
timeFormat="hh:mm a"
dateFormat="hh:mm a"
/>
}
>
</EuiDatePickerRange>
</EuiFormRow>
<EuiFormRow>
<EuiSwitch
label="Repeat Event"
checked={isDisabled}
onChange={(e) => setDisabled(e.target.checked)}
/>
</EuiFormRow>
<EuiFormRow>
<EuiDatePickerRange
startDateControl={
<EuiDatePicker
placeholder='Start'
disabled={!isDisabled}
selected={StartRepeatDate}
onChange={handleChangeStart}
startDate={StartRepeatDate}
endDate={EndRepeatDate}
isInvalid={StartRepeatDate > EndRepeatDate}
aria-label="Start date"
/>
}
endDateControl={
<EuiDatePicker
placeholder='End'
disabled={!isDisabled}
selected={EndRepeatDate}
onChange={handleChangeEnd}
startDate={StartRepeatDate}
endDate={EndRepeatDate}
isInvalid={StartRepeatDate > EndRepeatDate}
aria-label="End date"
/>
}
/>
</EuiFormRow>
<EuiFormRow>
<EuiRadioGroup
disabled={!isDisabled}
options={radios}
idSelected={radioIdSelected}
onChange={(id) => radioHandler(id)}
name="radio group"
/>
</EuiFormRow>
<EuiFormRow>
<EuiButton onClick={props.addEventHandler} type='submit'>
Add Event
</EuiButton>
</EuiFormRow>
</EuiForm>
</EuiFlexItem>
</EuiFlexGroup>
)
}
class Calendar extends Component {
state = {
selectInfo: null,
// dateClickInfo: null,
allDay: false,
title: null,
start_date: moment(),
end_date: moment(),
start_time: null,
end_time: null,
}
addEventHandler = (state) => {
if(this.state.title === null){
alert('Please enter title')
}
else if(this.state.selectInfo === null){
alert('Please select event date')
}
else {
if(this.state.start_time || this.state.end_time){
let calendarApi = this.state.selectInfo.view.calendar
calendarApi.addEvent({
title: this.state.title,
id: new Date(),
start: this.state.start_date.concat('T', this.state.start_time),
end: this.state.end_date.concat('T', this.state.end_time),
})
} else {
let calendarApi = this.state.selectInfo.view.calendar
calendarApi.addEvent({
title: this.state.title,
id: new Date(),
start: this.state.start_date,
end: this.state.end_date,
allDay: this.state.selectInfo.allDay,
})
}
}
}
titleHandler = (title) => {
const titleValue = title.target.value
this.setState({title: titleValue})
}
startDatePickerHandler = (date) => {
// date is moment object
const startDate = date.format().slice(0, 10)
const startTime = date.format().slice(11, 16)
// console.log('start date:' + startDate + ', start time:' + startTime)
this.setState({start_date: startDate, start_time: startTime})
}
endDatePickerHandler = (date) => {
// date is moment object
const endDate = date.format().slice(0, 10)
const endTime = date.format().slice(11, 16)
// console.log('end date:' + endDate + ', end time:' + endTime)
this.setState({end_date: endDate, end_time: endTime})
}
handleEventClick = (clickInfo) => {
if (window.confirm(`Are you sure you want to delete the event '${clickInfo.event.title}'`)) {
clickInfo.event.remove()
}
}
handleDateSelect = (selectInfo) => {
let calendarApi = selectInfo.view.calendar
calendarApi.unselect() // clear date selection
if(selectInfo) {
if (selectInfo.startStr.length >= 10){
const startStr = selectInfo.startStr.slice(0, 10)
const endStr = selectInfo.endStr.slice(0, 10)
const startTime = selectInfo.startStr.slice(11, 16)
const endTime = selectInfo.endStr.slice(11, 16)
this.setState({selectInfo: selectInfo, start_date: startStr, end_date: endStr, start_time: startTime, end_time: endTime})
} else {
this.setState({selectInfo: selectInfo, start_date:selectInfo.startStr, end_date: selectInfo.endStr})
}
}
}
render() {
return (
<EuiFlexGroup direction='row'>
<EuiFlexItem grow={6}>
<FullCalendar
plugins={[ dayGridPlugin, interactionPlugin, timeGridPlugin, googleCalendarPlugin ]}
googleCalendarApiKey = {process.env.REACT_APP_API_KEY}
events = {{
googleCalendarId: process.env.REACT_APP_GOOGLE_CALENDAR_ID
}}
headerToolbar={{
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay'
}}
initialView="dayGridMonth"
selectable={true}
editable={true}
expandRows={true}
select={this.handleDateSelect}
// dateClick={dateClickHandler}
eventClick={this.handleEventClick}
/>
</EuiFlexItem>
<EuiFlexItem grow={3}>
<AddEventBar
title={this.state.title}
start_date={this.state.start_date}
end_date={this.state.end_date}
start_time={this.state.start_time}
end_time={this.state.end_time}
titleHandler={this.titleHandler}
addEventHandler={this.addEventHandler}
startDatePickerHandler={this.startDatePickerHandler}
endDatePickerHandler={this.endDatePickerHandler}
></AddEventBar>
</EuiFlexItem>
</EuiFlexGroup>
)
}}
export default Calendar