-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: sync form state with inputs (fix #1233) #1235
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
import { h } from 'preact'; | ||
import { createPortal } from 'preact/compat'; | ||
import { useLayoutEffect, useMemo, useRef, useState } from 'preact/hooks'; | ||
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'preact/hooks'; | ||
|
||
import type { DateRangePicker } from 'tui-date-picker'; | ||
|
||
|
@@ -24,13 +24,13 @@ import { useFloatingLayer } from '@src/contexts/floatingLayer'; | |
import { useLayoutContainer } from '@src/contexts/layoutContainer'; | ||
import { cls } from '@src/helpers/css'; | ||
import { isLeftOutOfLayout, isTopOutOfLayout } from '@src/helpers/popup'; | ||
import { useFormState } from '@src/hooks/popup/useFormState'; | ||
import { FormStateActionType, useFormState } from '@src/hooks/popup/useFormState'; | ||
import type EventModel from '@src/model/eventModel'; | ||
import { calendarSelector } from '@src/selectors'; | ||
import { eventFormPopupParamSelector } from '@src/selectors/popup'; | ||
import TZDate from '@src/time/date'; | ||
import { compare } from '@src/time/datetime'; | ||
import { isNil } from '@src/utils/type'; | ||
import { isNil, isPresent } from '@src/utils/type'; | ||
|
||
import type { FormEvent, StyleProp } from '@t/components/common'; | ||
import type { BooleanKeyOfEventObject, EventObject } from '@t/events'; | ||
|
@@ -97,31 +97,11 @@ export function EventFormPopup() { | |
const { calendars } = useStore(calendarSelector); | ||
const { hideAllPopup } = useDispatch('popup'); | ||
const popupParams = useStore(eventFormPopupParamSelector); | ||
const { | ||
title, | ||
location, | ||
start, | ||
end, | ||
isAllday = false, | ||
isPrivate = false, | ||
eventState = 'Busy', | ||
popupArrowPointPosition, | ||
close, | ||
isCreationPopup, | ||
event, | ||
} = popupParams ?? {}; | ||
const { start, end, popupArrowPointPosition, close, isCreationPopup, event } = popupParams ?? {}; | ||
const eventBus = useEventBus(); | ||
const formPopupSlot = useFloatingLayer('formPopupSlot'); | ||
const [formState, formStateDispatch] = useFormState({ | ||
title, | ||
location, | ||
start, | ||
end, | ||
isAllday, | ||
isPrivate, | ||
calendarId: event?.calendarId ?? calendars[0]?.id, | ||
state: eventState, | ||
}); | ||
const [formState, formStateDispatch] = useFormState(calendars[0]?.id); | ||
|
||
const datePickerRef = useRef<DateRangePicker>(null); | ||
const popupContainerRef = useRef<HTMLDivElement>(null); | ||
const [style, setStyle] = useState<StyleProp>({}); | ||
|
@@ -157,6 +137,30 @@ export function EventFormPopup() { | |
} | ||
}, [layoutContainer, popupArrowPointPosition]); | ||
|
||
// Sync store's popupParams with formState when editing event | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 팝업이 열릴 때, 이벤트를 수정하는 상태일 경우 |
||
useEffect(() => { | ||
if (isPresent(popupParams) && isPresent(event)) { | ||
formStateDispatch({ | ||
type: FormStateActionType.init, | ||
event: { | ||
title: popupParams.title, | ||
location: popupParams.location, | ||
isAllday: popupParams.isAllday, | ||
isPrivate: popupParams.isPrivate, | ||
calendarId: event.calendarId, | ||
state: popupParams.eventState, | ||
}, | ||
}); | ||
} | ||
}, [calendars, event, formStateDispatch, popupParams]); | ||
|
||
// Reset form states when closing the popup | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. formState를 제대로 활용하기 전에는 팝업을 닫자마자 input 태그의 값이 모두 날아가버렸기 때문에 이런 과정이 필요 없었습니다. 하지만 formState의 상태와 input 태그의 value를 연결했기 때문에 제어가 필요합니다. |
||
useEffect(() => { | ||
if (isNil(popupParams)) { | ||
formStateDispatch({ type: FormStateActionType.reset }); | ||
} | ||
}, [formStateDispatch, popupParams]); | ||
|
||
if (isNil(start) || isNil(end) || isNil(formPopupSlot)) { | ||
return null; | ||
} | ||
|
@@ -198,11 +202,11 @@ export function EventFormPopup() { | |
<PopupSection /> | ||
)} | ||
<TitleInputBox | ||
title={title} | ||
title={formState.title} | ||
isPrivate={formState.isPrivate} | ||
formStateDispatch={formStateDispatch} | ||
/> | ||
<LocationInputBox location={location} /> | ||
<LocationInputBox location={formState.location} formStateDispatch={formStateDispatch} /> | ||
<DateSelector | ||
start={start} | ||
end={end} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,36 +3,61 @@ import { useReducer } from 'preact/hooks'; | |
import type { EventObject, EventState } from '@t/events'; | ||
|
||
export enum FormStateActionType { | ||
init = 'init', | ||
setCalendarId = 'setCalendarId', | ||
setTitle = 'setTitle', | ||
setLocation = 'setLocation', | ||
setPrivate = 'setPrivate', | ||
setAllday = 'setAllday', | ||
setState = 'setState', | ||
reset = 'reset', | ||
} | ||
|
||
type FormStateAction = | ||
| { type: FormStateActionType.init; event: EventObject } | ||
| { type: FormStateActionType.setCalendarId; calendarId: string } | ||
| { type: FormStateActionType.setTitle; title: string } | ||
| { type: FormStateActionType.setLocation; location: string } | ||
| { type: FormStateActionType.setPrivate; isPrivate: boolean } | ||
| { type: FormStateActionType.setAllday; isAllday: boolean } | ||
| { type: FormStateActionType.setState; state: EventState }; | ||
| { type: FormStateActionType.setState; state: EventState } | ||
| { type: FormStateActionType.reset }; | ||
|
||
export type FormStateDispatcher = (action: FormStateAction) => void; | ||
|
||
const defaultFormState: EventObject = { | ||
title: '', | ||
location: '', | ||
isAllday: false, | ||
isPrivate: false, | ||
state: 'Busy', | ||
}; | ||
|
||
// eslint-disable-next-line complexity | ||
function formStateReducer(state: EventObject, action: FormStateAction): EventObject { | ||
switch (action.type) { | ||
case FormStateActionType.init: | ||
return { ...defaultFormState, ...action.event }; | ||
case FormStateActionType.setCalendarId: | ||
return { ...state, calendarId: action.calendarId }; | ||
case FormStateActionType.setTitle: | ||
return { ...state, title: action.title }; | ||
case FormStateActionType.setLocation: | ||
return { ...state, location: action.location }; | ||
case FormStateActionType.setPrivate: | ||
return { ...state, isPrivate: action.isPrivate }; | ||
case FormStateActionType.setAllday: | ||
return { ...state, isAllday: action.isAllday }; | ||
case FormStateActionType.setState: | ||
return { ...state, state: action.state }; | ||
case FormStateActionType.reset: | ||
return { ...state, ...defaultFormState }; | ||
|
||
default: | ||
return state; | ||
} | ||
} | ||
|
||
export function useFormState(initialState: EventObject) { | ||
return useReducer(formStateReducer, initialState); | ||
export function useFormState(initCalendarId?: string) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. calendarId가 없으면 기본값을 |
||
return useReducer(formStateReducer, { calendarId: initCalendarId, ...defaultFormState }); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
애초에
useFormState
선언 시 기본값을 주는 것은 거의 의미가 없습니다. 컴포넌트 마운트 시 최초에 한 번만 실행되기 때문입니다. 그런데 그 때는 대부분의 값이 비어있겠죠.