์๋ก
์ ๋ง ๋ช๋ ๋ฉฐ์น ์ ๋๋ฒ๊น ํ์ง๋ง ํด๊ฒฐ ์๋๋ ์ด์๊ฐ ์์๋ค...
Mui ํ๋ ์์ํฌ ์์ฒด์ ๋ฌธ์ ์ธ๊ฑธ๊น? ํ๊ณ Textfield ์ปดํฌ๋ํธ๋ฅผ ์์ ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ก ๊ฐ์์๊ธฐ ์ ๋ง์ง๋ง์ผ๋ก ํ๋ฒ๋ง ๋ ๋๋ฒ๊น ํด๋ณด์ ํ๋๋ฐ ์ค๋ ์ง์ง ์ด์ด์๊ฒ ์ด์๊ฐ ํด๊ฒฐ๋์๋ค... ๋ด๊ฐ ์ด๊ฒ๋๋ฌธ์ ๋ช์ผ์ ๊ณ ์ํ๋ค๊ณ .... ๐ญ
์ฌ๋ด ํ๋ก์ ํธ๋ ๋ฆฌ์กํธ ui ์ปดํฌ๋ํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก MUI๋ฅผ ์ฌ์ฉํ๋ค.
MUI ๊ณต์๋ฌธ์์์ ํํ๋์ด์๋๋๋ก, ์ธํ๋ฐ์ค๋ฅผ Textfield ์ปดํฌ๋ํธ๋ก ํํํ๊ณ , input ๋ฐ์ค ์ค๋ฅธ์ชฝ ๋์์๋ x ๋ฒํผ์ InputProps์ endAdornment ์์ฑ์ผ๋ก ๊ฐ์ธ์ฃผ์ด ๊ตฌ์กฐ๋ฅผ ์ก์๋ค.
<TextField
label='์ด๋ฆ'
InputProps={{
endAdornment: (
<InputAdornment position='end'>
{nameValue.length > 0 && isNameFocused && (
<IconButton
size='small'
onClick={onClickClearNameValue}
edge='end'>
<CancelIcon />
</IconButton>
)}
</InputAdornment>
)
}}
/>
Expected Behaviour
์ธํ๋ฐ์ค๋ฅผ ๋๋ฅด๋ฉด focus ์ฒ๋ฆฌ๋๊ณ , ์ธํ๋ฐ์ค์ ๋ฐ๊นฅ ์์ญ์ ๋๋ฅด๋ฉด blur ์ฒ๋ฆฌ๋๋ค.
๋ฐ๊นฅ ์์ญ์ ๋๋ฅด๋ ๋ธ๋ฌ์ฒ๋ฆฌ ์ํ์์๋ input ๋ฐ์ ์๋ helper text ๋ฐ ์ธํ ์ค๋ฅธ์ชฝ ์ x ๋ฒํผ์ ๋ณด์ด์ง ์๋๋ค.
์ธํ ์์ ์๋ x๋ฒํผ์ ๋๋ฅด๋ฉด ์ธํ ์์ ๋ด์ฉ์ด ์ด๊ธฐํ๊ฐ ๋๋ค.
Current Behaviour
๋ฌธ์ ๋ x ๋ฒํผ์ ๋๋ฅด๋ฉด IconButton์ ๋ฐ์ธ๋ฉํ onClickClear ํจ์๊ฐ ์คํ์ด ์ -ํ ์๋๋ค.
์ด์ํ๊ฒ onBlur ํจ์๋ง ์คํ๋์ด์ input ๋ด์ฉ์ด ์ง์์ง๋๊ฒ ์๋๊ณ ๊ทธ๋ฅ focus out ์ฒ๋ฆฌ๋๋ค. (์ ํํ ๋งํ๋ฉด blur ์ฒ๋ฆฌ๋๋ค.)
+ ์ฐธ๊ณ
focus out๊ณผ blur ์ด๋ฒคํธ๋ ํฌ์ปค์ค๋ฅผ ์๊ฒํ๋ ๊ธฐ๋ฅ์ ํ์ง๋ง, ์ ํํ ๋งํ๋ฉด focus out์ DOM ์์์ ๋ฒ๋ธ๋ง์ด ๋ฐ์ํ๊ณ blur๋ ๋ฐ์ํ์ง ์๋๋ค. ๋ฆฌ์กํธ์์๋ focus out ์ด๋ฒคํธ ๋์ ์ blur ์ด๋ฒคํธ ์ฌ์ฉ์ ๊ถ์ฅํ๋ค. ๊ฐ์ ๋งฅ๋ฝ์ผ๋ก ํฌ์ปค์ฑ์ ์ก์๋๋ focus in ๋์ ์ focus ์ด๋ฒคํธ๊ฐ ์ฌ์ฉ๋๋ค. (์ ์ด์ ์ฝ๋ฉ ์๋์์ฑ ํด์ค๋ ์ธ ์ ์๋ ์ด๋ฒคํธ ํจ์์ onFocusIn onFocusOut์ ์กด์ฌํ์ง์๊ณ onFocus์ onBlur ๋ฐ์ ์๋ค.)
์ฒ์ฐธํ ์ํฉ...
gif ์บก์ณ๋จ๋ฉด์ ๋ง์ฐ์ค ์์น๊ฐ ์ ๋ณด์ด์ง ์๋๋ฐ, x ๋ฒํผ์ ๋๋ฅด๋ฉด ์ธํ ๋ด์ฉ์ด ์ฌ๋ผ์ง๋๊ฒ ์๋๊ณ Blur ์ด๋ฒคํธ๊ฐ ์คํ๋์ ํฌ์ปค์ฑ ์์ ์ฒ๋ฆฌ๊ฐ ๋๋ ์ํฉ์ด๋ค.
x ๋ฒํผ์ ๋๋ฅด๋ฉด Blur ํจ์๋ง ์คํ๋๊ณ ์๋ค๋ ๊ฑธ ์ฝ์์ผ๋ก ํ์ธํ ์ ์๋ค.
์ ์ํฉ์ ์ฝ๋๋ ๊ฐ๋จํ ์๋์ ๊ฐ๋ค.
export const NameInput = (props: Props) => {
const [nameValue, setNameValue] = useState('');
const handleChangeNameValue = (e: React.ChangeEvent<HTMLInputElement>) => {
setNameValue(e.target.value);
};
const handleClickClearNameValue = (e: React.MouseEvent) => {
console.log('clear ํจ์๋์');
setNameValue('');
};
const [isNameFocused, setIsNameFocused] = useState(true);
const nameInputRef = useRef<HTMLInputElement>(null);
const handleNameInputFocus = () => {
console.log('Focus ํจ์');
nameInputRef.current?.focus();
setIsNameFocused(true);
};
const handleNameInputBlur = () => {
console.log('Blur ํจ์');
nameInputRef.current?.blur();
setIsNameFocused(false);
};
return (
<TextField
id='nameInput'
variant='filled'
label='์ด๋ฆ'
fullWidth
InputProps={{
endAdornment: (
<InputAdornment position='end'>
{nameValue.length > 0 && isNameFocused && (
<IconButton
onClick={handleClickClearNameValue}
edge='end'>
<CancelIcon />
</IconButton>
)}
</InputAdornment>
),
}}
autoFocus
inputRef={nameInputRef}
onFocus={handleFocusNameInput}
onBlur={handleBlurNameInput}
value={nameValue}
onChange={handleChangeNameValue}
/>
)
์ฌ๋ฌ๊ฐ์ง ๊ณ ๋ฏผ๊ณผ ์๋๋ฅผ ํด๋ดค์๋ค.
- div ์ธํ ๋ฐ์ค
- input ์ธํ
- button ์์ด์ฝ๋ฒํผ
textfield ๊ตฌ์กฐ๊ฐ ์ด๋ฐ์์ด๊ณ , x๋ฒํผ์ด ๊ฐ์ฅ ์์ ์ธํ ๋ฐ์ค div ํ์์ ์๋ ์์์ด๋ input์ ํฌ์ปค์คํ๋ onFocus ์ด๋ฒคํธ์์ ํ์์์๊น์ง ์บก์ฒ๋ง์ ์ผ์ด๋๊ฒํ๋ onFocusCapture๋ฅผ ์ ์ฉํด์ผ๋๋? ์์๋๋ก ๋์ง์์
๋ญ๊ฐ mui ์์ฒด์์ ์์ input ์์ญ์ ์ ์ธํ ์์ญ์ ๋ชจ๋ ๋ธ๋ฌ์ฒ๋ฆฌํด์ adornment ์์ญ๊น์ง ๋ธ๋ฌ์ฒ๋ฆฌ ํด๋ฒ๋ฆฌ๋ ๋ฒ๊ทธ์ธ๊ฐ..? ๊ตฌ๊ธ๋งํด๋ดค์ง๋ง ๊ด๋ จ ์ผ์ด์ค ๋ฌด..
Fix1.
๊ฒฐ๊ตญ ๋ฌธ์ ๋ ๋ฒํผ์ ๋๋ฅด๋ฉด onClick ์ด๋ฒคํธ ๋ฐ์์ด ์๋๊ณ onBlur ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ค๋ ๊ฒ.
๋ ํค์๋๋ฅผ ์ฃผ์ ๋ฅผ ์ก๊ณ ๊ตฌ๊ธ๋งํ๋ ์์๊ฐ์ ํด๊ฒฐ์ด ๋์๋ค.
์ด ์ฃผ์ ๋ก๋ ๊ตฌ๊ธ๋ง ์๋ฃ๊ฐ ๋ง๊ณ stackoverflow์๋ ๋ง์ด ๋ ผ์๋๋ ์ฃผ์ ์๋๋ฐ, ๋ฐ๋ก ๋ฌธ์ ๋... onClick ์ด๋ฒคํธ์ onBlur ์ด๋ฒคํธ์ ์คํ ์์์ ์์๋ค.
onBlur ์ด๋ฒคํธ๊ฐ onClick ์ด๋ฒคํธ ๋ณด๋ค ํญ์ ๋จผ์ ์คํ๋์ ์๊ธฐ๋ ๋ฌธ์ ์ด๋ค.
ํด๊ฒฐ์ฑ ์ onClick ์ด๋ฒคํธ ๋์ ์ onMouseDown ์ด๋ฒคํธ๋ฅผ ์ฌ์ฉํ๋ฉด ํด๊ฒฐ๋๋ค๋ ๊ฒ์ด๋ค.
MDN์์ ๋ช ์๋์ด์๋๋๋ก click ์ด๋ฒคํธ๋ mousedown -> mouseup -> click ์ด๋ฒคํธ ์์๋๋ก ๋ฐ์ํ๋ค.
click fires after both the mousedown and mouseup events have fired, in that order.
๋ฐ๋ผ์ blur ์ด๋ฒคํธ๋ฅผ ๋ค์ ๋ฐ์์ํค๊ณ , ํด๋ฆญ์ด ๋จผ์ ๋๊ฒ ํ๊ณ ์ถ์ผ๋ฉด, mousedown ์ด๋ฒคํธ๋ก ์ฒ๋ฆฌํด์ฃผ๋ฉด ๋๋ค.
<IconButton
onMouseDown={handleClickClearNameValue} <--- HERE
edge='end'>
<CancelIcon />
</IconButton>
onMouseDown ์ด๋ฒคํธ๋ก ๋ฐ๊ฟ์ฃผ๋ฉด ๋.
์ด์ clear ํจ์๊ฐ blur ํจ์ ์ด์ ์ ํธ์ถ๋๋ฉด์ ์ธํ ๋ด์ฉ์ ์ ์ง์ฐ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.!!! ๐
ํ์ง๋ง...
๊ธฐ์จ๋ ์ ์...
์, clear ํจ์๊ฐ ์คํ๋๋ฉด ์ฐ๋ฆฌ๊ฐ ์ํ๋๊ฑด ์ธํ ๋ด์ฉ์ ์ง์ฐ๊ณ , ํฌ์ปค์ค๊ฐ ์ธํ์ ๊ทธ๋๋ก ๋จ์์๊ธธ ์ํ๋ค. ๊ทธ๋์ผ ๋ค๋ฅธ ๋ด์ฉ์ ๊ณ์ ์ธ์ ์์ผ๋๊น.
๊ทธ๋ฐ๋ฐ ์ง๊ธ๋ณด๋ฉด clear ํจ์๊ฐ ์คํ๋๊ณ ๋ฐ๋ก ๊ทธ ๋ค์ blur ํจ์๊ฐ ์คํ๋์ด ํฌ์ปค์ค๊ฐ ์ฌ๋ผ์ง๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์ด๊ฑด ์ด๋ป๊ฒ ํด๊ฒฐํ ๊น?
Fix2.
clear ํจ์๊ฐ ์คํ๋๊ณ , blurํจ์๊ฐ ์คํ๋๋ ๊ฒ์ ๋ง์ผ๋ฉด ๋๋ค.
mousedown ์ด๋ฒคํธ๋ blur ์ด๋ฒคํธ์ ํจ๊ป ์ฐ์ผ๋ ๊ธฐ๋ณธ๊ฐ์ผ๋ก blur ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํค๋ฏ๋ก, ์ด๋ฒคํธ์ ๊ธฐ๋ณธ๋์์ ์ทจ์ํ๊ธฐ์ํด ์ด๋ฒคํธ ๊ฐ์ฒด์ preventDefault() ๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ blur ํจ์ ์ด๋ฒคํธ๋ฅผ ๋ง๋๋ค.
const handleClickClearNameValue = (e: React.MouseEvent) => {
e.preventDefault(); <-------- HERE
console.log('clear ํจ์๋์');
setNameValue('');
};
์ด์ x๋ฒํผ์ ๋๋ฅด๋ฉด clear ํจ์๊น์ง๋ง ๋์ํ๊ณ ํฌ์ปค์ค๋ ๊ทธ๋๋ก ์ธํ์ ๋จ์ ์์์ ํ์ธํ ์ ์๋ค.
์ ์ฒด์ฝ๋
export const NameInput = (props: Props) => {
const [nameValue, setNameValue] = useState('');
const handleChangeNameValue = (e: React.ChangeEvent<HTMLInputElement>) => {
setNameValue(e.target.value);
};
const handleClickClearNameValue = (e: React.MouseEvent) => {
e.preventDefault();
console.log('clear ํจ์๋์');
setNameValue('');
};
const [isNameFocused, setIsNameFocused] = useState(true);
const nameInputRef = useRef<HTMLInputElement>(null);
const handleNameInputFocus = () => {
console.log('Focus ํจ์');
nameInputRef.current?.focus();
setIsNameFocused(true);
};
const handleNameInputBlur = () => {
console.log('Blur ํจ์');
nameInputRef.current?.blur();
setIsNameFocused(false);
};
return (
<TextField
id='nameInput'
variant='filled'
label='์ด๋ฆ'
fullWidth
InputProps={{
endAdornment: (
<InputAdornment position='end'>
{nameValue.length > 0 && isNameFocused && (
<IconButton
onMouseDown={handleClickClearNameValue}
edge='end'>
<CancelIcon />
</IconButton>
)}
</InputAdornment>
),
}}
autoFocus
inputRef={nameInputRef}
onFocus={handleFocusNameInput}
onBlur={handleBlurNameInput}
value={nameValue}
onChange={handleChangeNameValue}
/>
)
๊ฒฐ๋ก
๋ ํ๋ฐํธ ๊ฐ๋ฐ์๋๊น ์ ๋ง ์ค๋ฌด์์ ์ ๋๋ก ์ฐ๋ ค๋ฉด ์ด๋ฒคํธ๋ DOM ์ ๋ ๊น์ด ์์์ผ๋๋ค๋ ๊ฑธ ๋ผ์ ๋ฆฌ๊ฒ ๋๋.
์ฝ๋ ํ ์ค ์๋ ์ง์ฌ ํ ๋จ์ด๋ง ๊ณ ์น๋ ๋ฉฐ์น ์ ๊ณ ๋ฏผํ ์ด์๊ฐ ํ๋ฒ์ ์ฌ๋ผ์ ธ์ ์ข ํ๋ฌดํ๊ธฐ๋ ํ์ง๋ง.. CTO๋ ํํ
๋ ๋ฆฌํฌํธํ ์ด์์๋จ ๋ง์ด์ฌ...
ํด๊ฒฐ๋๋ ์๊ฐ์ ํฌ์ด์ด ๋ ์ฝ๋ฉ์ ๋ฌ๋ฏธ์ธ ๊ฒ ... ์ฆ๊ฑฐ์ ๋ฐ.
References
https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event
https://www.w3schools.com/jsref/event_onmousedown.asp
https://coffeeandcakeandnewjeong.tistory.com/70