nhn / tui.calendar

🍞📅A JavaScript calendar that has everything you need.
http://ui.toast.com/tui-calendar
MIT License
12.03k stars 1.3k forks source link

When resizing event `beforeUpdateEvent` called multiple times in month view #1280

Open FFloou opened 2 years ago

FFloou commented 2 years ago

Version

v.2.1.2

Test Environment

Chrome

Current Behavior

// 'onBeforeUpdateEvent' logged 5 times in console when I resize event
const onBeforeUpdateEvent: ExternalEventTypes['beforeUpdateEvent'] = res=>{
    consle.log('onBeforeUpdateEvent');
}

<Calendar onBeforeUpdateEvent={onBeforeUpdateEvent}/>

Expected Behavior

I'm trying to use my own update event function with onBeforeUpdateEvent. Dragging to another day works well... But when resize event,onBeforeUpdateEvent called 5 times so my custom update event happens multiple times too. I'm wondering is there any solution to solve this problem.

adhrinae commented 2 years ago

@FFloou

I couldn't reproduce the problem you mentioned. Can you provide any example code reproducing the same problem?

FYI it might be related to the equality check between props while re-render.

https://github.com/nhn/tui.calendar/blob/main/apps/react-calendar/docs/en/guide/getting-started.md#%EF%B8%8F-note-for-passing-props

adhrinae commented 2 years ago

Closing since there's been no feedback and it cannot be reproduced.

erlandsona commented 1 year ago

Sorry to necro-bump this issue but I'm seeing the same behavior unfortunately. I'm using the react component via a webcomponent wrapper called by an Elm app and the React component is only being rendered once as the calendar instance is in a ref.

But when I drag-n-drop the drag handle of an existing event the onBeforeUpdateEvent callback is being fired multiple times.

Here's the component

const TuiCalendar = ({
  timescale: view,
  events, cursor, toElm: toElm_, dialogClosed
}) => {

  const tuiRef = useRef(null)
  const tui = useCallback(() => tuiRef.current?.getInstance?.(), [])

  useEffect(() => tui().setDate(cursor), [tui, cursor])

  useEffect(() => {
    if (dialogClosed) {
      tui().clearGridSelections()
    }
  }, [tui, dialogClosed])

  const toElm = useCallback((type, body) => {
    toElm_(`tui_${ type }`, body)
  }, [toElm_])

  const onClickEvent = useCallback(({ event }) => {
    toElm('event_clicked', event)
  }, [toElm])

  const onSelectDateTime = useCallback((evt) => {
    const start = evt.start.getTime()
    const end = evt.end.getTime()
    toElm('range_selected', { ...evt, start, end })
  }, [toElm])

  const onBeforeUpdateEvent = useCallback((updates) => {
    const { event, changes } = updates
    tui().updateEvent(event.id, event.calendarId, changes)

    const start = (changes.start ?? event.start)?.getTime()
    const end = (changes.end ?? event.end)?.getTime()

    toElm('range_updated', { ...event, start, end })
  }, [tui, toElm])

  return (
    <Tui
      // Config
      ref={ tuiRef }
      height="100%"
      view={ view }
      events={ events }

      // Callbacks / Handlers
      onSelectDateTime={ onSelectDateTime }
      onClickEvent={ onClickEvent }

      // Mutations
      onBeforeUpdateEvent={ onBeforeUpdateEvent }
    />
  )
}
TuiCalendar.propTypes = Tui.propTypes
adhrinae commented 1 year ago

@erlandsona I wonder if the TuiCalendar component's props bypass the wrapper's equality check.

Can you give us any sample sandbox(codesandbox, stackbtliz, etc.) to reproduce this problem?

kipa-zhang commented 1 year ago

image

kipa-zhang commented 1 year ago

Same problem image

adhrinae commented 1 year ago

I confirmed that It occurs when you resize any all-day event in the month view.

There might be a problem in this function but I cannot look deeper at this moment.

Ollie222 commented 1 year ago

I'm new to using ToastUI Calendar but I've just come across this same issue.

As discussed it only happens when resizing events and it can be for an all day or timed event.

The problem looks to be that the function ResizingGuideByRow() is called once per row and this calls useDayGridMonthEventResize() which runs the beforeUpdateEvent.

You can see this if you setup the calendar with isAlways6Weeks: false and then on a month that shows 4 or 5 rows the number of calls to beforeUpdateEvent is the same.

I've only looked at the code briefly and I can't see exactly how it's supposed to be working but to me it seems that either:-

  1. All of the rows should be processed and then the beforeUpdateEvent should be called once.
  2. The 'shouldUpdate' check that surrounds the beforeUpdateEvent should be changed to take into account the rowIndex so the beforeUpdateEvent is only called when it matches the event being resized.

In useDayGridMonthEventResize() if you change the line

const shouldUpdate = !isDraggingCanceled && (currentGridPos.rowIndex === eventStartDateRowIndex && currentGridPos.columnIndex >= eventStartDateColumnIndex || currentGridPos.rowIndex > eventStartDateRowIndex);

to

const shouldUpdate = !isDraggingCanceled && (rowIndex === eventStartDateRowIndex && (currentGridPos.columnIndex >= eventStartDateColumnIndex || currentGridPos.rowIndex > eventStartDateRowIndex));

then this seems to fix the problem however it seems to me that a rowIndex check should be taking place much earlier.

FFloou commented 9 months ago

I found that this issue is still opened... This issue seems a problem of calendar itself and it takes some time to be fixed.

What I wanted was fetching API only once which included in update function, so I ended up wrapping with debounce.

const onBeforeUpdateEvent: ExternalEventTypes['beforeUpdateEvent'] = _.debounce(res=>{
    consle.log('onBeforeUpdateEvent');
}, 50)
iwtem commented 1 month ago

我是 ToastUI 日历的新手,但刚刚遇到同样的问题。

正如所讨论的,它仅在调整事件大小时发生,并且可以用于全天或定时事件。

问题似乎是,函数 ResizingGuideByRow() 每行调用一次,并且这会调用 useDayGridMonthEventResize(),后者运行 beforeUpdateEvent。

如果您使用 isAlways6Weeks: false 设置日历,则可以看到这一点,并且在显示 4 或 5 行的月份中,对 beforeUpdateEvent 的调用次数是相同的。

我只是简单地看了一下代码,看不出它到底是如何工作的,但对我来说,它似乎是:-

  1. 应该处理所有行,然后调用一次 beforeUpdateEvent。
  2. 应该将围绕 beforeUpdateEvent 的“shouldUpdate”检查更改为考虑 rowIndex,以便仅当它与正在调整大小的事件匹配时才调用 beforeUpdateEvent。

在 useDayGridMonthEventResize() 中,如果您更改行

const shouldUpdate = !isDraggingCanceled && (currentGridPos.rowIndex === eventStartDateRowIndex && currentGridPos.columnIndex >= eventStartDateColumnIndex || currentGridPos.rowIndex > eventStartDateRowIndex);

const shouldUpdate = !isDraggingCanceled && (rowIndex === eventStartDateRowIndex && (currentGridPos.columnIndex >= eventStartDateColumnIndex || currentGridPos.rowIndex > eventStartDateRowIndex));

那么这似乎解决了问题,但是在我看来,rowIndex 检查应该更早进行。

This solution works