MyPage 컴포넌트에는 boolean 값을 가지는 상태가 2개가 있다.
isEditing
isModalOpen
두 상태 모두 같은 기능을 하는 토글 상태이므로 재사용 가능한 useToggle
훅을 만들어서 컴포넌트를 간소화해보려고 한다.
const MyPage = () => {
const [isEditing, setIsEditing] = useState(false); // 프로필 수정 버튼을 눌러 편집창을 껐다 켰다 할 수 있는 상태
const [isModalOpen, setIsModalOpen] = useState(false); // 모달을 껐다 켰다 할 수 있는 상태. 모달에는 로그아웃, 회원탈퇴 버튼이 있음
// 프로필을 수정하고 변경한 내용을 submit하는 함수
const onSubmit = () => {
// 생략...
setIsEditing(false);
}
// 프로필 수정창에서 '취소' 버튼에 연결된 이벤트 핸들러
const onCancel = () => {
setIsEditing((isEditing) => !isEditing);
}
// 모달 껐다 켰다할 수 있는 버튼에 연결된 이벤트 핸들러
const handleModal = () => {
setIsModalOpen((isModalOpen) => !isModalOpen);
}
// 모달 영역 밖을 클릭하면 isModalOpen을 false로 업데이트해주는 함수
const handleOutsideClick = (e: React.MouseEvent<HTMLDivElement>) => {
const target = e.target as any;
if (isModalOpen && !modalRef.current?.contains(target)) {
setIsModalOpen(false);
}
};
return (// 생략...);
}
import { useState } from 'react';
function useToggle(initialValue = false) { // 초기값을 매개변수로 받는다. 초기값이 없는 경우 기본값으로 false를 준다.
const [status, setStatus] = useState<boolean>(initialValue);
const toggleStatus = () => setStatus((prevStatus) => !prevStatus);
return [status, toggleStatus];
}
export default useToggle;
const MyPage = () => {
const [isEditing, toggleIsEditing] = useToggle(false);
const [isModalOpen, toggleIsModalOpen] = useToggle(false);
// 프로필을 수정하고 변경한 내용을 submit하는 함수
const onSubmit = () => {
// 생략...
toggleIsEditing();
}
// 프로필 수정창에서 '취소' 버튼에 연결된 이벤트 핸들러
const onCancel = () => {
toggleIsEditing();
}
// 모달 껐다 켰다할 수 있는 버튼에 연결된 이벤트 핸들러
const handleModal = () => {
toggleIsModalOpen();
}
// 모달 영역 밖을 클릭하면 isModalOpen을 false로 업데이트해주는 함수
const handleOutsideClick = (e: React.MouseEvent<HTMLDivElement>) => {
toggleIsModalOpen();
};
return (// 생략...);
}
매우 매우 직관적이고 깔끔해졌다.!
하지만 이렇게 하고 실행하니 에러를 만났는데..
This expression is not callable.
Not all constituents of type 'boolean | (() => void)' are callable.
Type 'false' has no call signatures.
27 |
28 | const handleModal = () => {
> 29 | toggleIsModalOpen();
| ^^^^^^^^^^^^^^^^^
30 | };
이 에러는 함수로 간주되지 않는 타입에 대해 호출 연산자()
를 사용하려고 할 때 발생한다.
즉, toggleIsModalOpen()
이 타입스크립트에게 함수로 인식되고있지 않다는 뜻이다.
그 이유는 useToggle
훅에서 상태와 함수를 배열로 반환하고 있기 때문이다..
return [status, toggleStatus]
그래서 아래와 같이 useToggle
훅에 타입 지정을 추가해줬더니, 타입스크립트가 반환 타입을 추론할 수 있게 되며 에러가 사라졌다.
import { useState } from 'react';
type ToggleHookType = [boolean, () => void]; // 타입 추가
function useToggle(initialValue = false): ToggleHookType { // 타입 지정
const [status, setStatus] = useState<boolean>(initialValue);
const toggleStatus = () => setStatus((prevStatus) => !prevStatus);
return [status, toggleStatus];
}
export default useToggle;
함수로 인식안하면 당황스럽던데 잘 해결하셨네요 고생하셨습니다.