useResize는 화면의 사이즈가 변화하여 요소의 view 크기가 변경되었을 때 원하는 동작을 수행할 수 있도록 하는 hook이다. 요소의 크기가 변화함을 감지하기 위하여 web api인 ResizeObserver를 이용한다.
observer.observe(myRefObject)
그동안 useIntersectionObserver만 사용해봤는데, 다양한 dom의 상태를 감지하는 여러 api가 존재함을 알았다. 이를 잘 활용한다면, 강력한 홈페이지를 만들 수 있을 것 같다.
useLocalStorage는 localStorage에서 값을 꺼내고 저장하는 역할을 하는 hook이다. 이전에 만든 hook과 동일하므로 한번 복습하는 차원으로 만들어 보았다.
sessionStorage와 localStorage api 사용법이 완전히 동일하기 때문에, sessionStorage는 따로 만들어보진 않았다. 근데, useStorage 하나만 사용하고, 외부에서 모드에 따라 localStorage와 sessionStorage를 선택할 수 있도록 만드는 방법도 좋을 것 같다고 생각한다.
useForm hook은 form관 관련된 로직을 처리하기 위하여 만든 hook이다.
form을 처리하는 많은 라이브러리들이 있으나, form 데이터 처리를 위해 필요한 것들을 파악하고, 처리 로직에 대해 이해해보고자 직접 작성하였다.
form을 처리하기 위해서는 다음의 역할들을 수행할 수 있어야 한다.
handleSubmit 호출 시, 만약 validate 함수가 있다면 validate를 수행한다. 만약 아무 에러가 존재하지 않다면 await submit(values)
를 실행하고, 그렇지 않다면 error를 저장한다.
이전에 만든 useInput에 비하여 조금 더 발전된 형태의 hook 같다. 데이터를 입력 받는 부분과 데이터가 변화할 때 처리하는 콜백 함수를 useInput으로 따로 분리하여 useInput의 사용성을 증가시키는 방법도 좋을 것 같다.
여기서부터 약간 뇌정지가 옴
타임아웃 로직을 실행하기 위한 hook이다. useTimeout hook을 만들기에 앞서 useTimeoutFn hook을 먼저 만들어 재사용성을 극대화 하고자 하였다. useTimeoutFn은 다음의 역할을 수행한다.
주의해야 할 점은, 컴포넌트의 재생성 및 컴포넌트가 파괴가 될 때를 생각하여 useEffect로 clear를 호출해주어야 원하지 않는 sideEffect가 발생하지 않는다.
useTimeFn을 직접 사용할 경우, 사용자가 원하는 시점에 setTimeout을 실행할 수 있으며, useTimeoutFn으로부터 만든 useTimeout hook을 사용할 경우, 해당 컴포넌트가 화면에 표시되면 바로 setTimeout 타이머가 실행된다.
사실 setTimeout을 바로 실행하는 방식으로 로직을 작성하는 법은 알았는데, 이것을 분리하여 재사용성을 극대화 하는 방법은 굉장히 참신하다고 생각했다. (사실 조금만 더 고민하면 생각해낼 수 있을 텐데 이런 부분이 조금 부족한듯 하다)
useInterval 역시 useTimeout과 만드는 방법이 비슷했다. 마찬가지로 useIntervalFn을 먼저 만든 뒤, useInterval은 useInterval로부터 전달받은 함수들을 이용하도록 하였다.
에러 고치는데 거의 한시간 썼음
useTimeoutFn으로부터 useDebounce 함수를 만들 수 있다고 생각해서 useTimeout을 바탕으로 useDebounce 함수를 작성했는데, 생각대로 동작하지 않았다.
// 기존 코드
const useDebounce = (cb, deps, ms) => {
const [run, clear] = useTimeoutFn(cb, ms)
useEffect(run, [run, ...deps])
}
이 방식으로 코드를 작성하였고, 테스트 할 컴포넌트는 다음과 같이 작성했다.
const Test = () => {
// debounce
//...
return (
<div>
{debounceResult}
<input type="text" onChange={(e) => {
setValue(e.target.value)
}
}
/>
</div>
)
}
deps에 value를 참조하도록 하여, value가 갱신될 때마다 debounce가 실행되도록 하였다. 이 방식으로 작성했을 때, debounce 로직 자체는 잘 실행되나(console.log를 찍어보면 세팅한 시간이 지난 다음에 찍히는 것을 확인할 수 있었다.) value의 값이 갱신되지 않는 문제가 있었다.
솔직히 이 방식도 썩 맘에 드는 것은 아니지만 일단 동작은 하기 때문에 이 방식을 선택하기로 했다. 나중에 고칠 수 있으면 한번 더 고쳐야겠다. (역시 강의랑 다른 방법으로 하면 이런 문제가 생길 수 있구나...)
// 고친 코드
const useDebounce = <T>(value: T, ms = 0) => {
const [debouncedValue, setDebouncedValue] = useState(value);
const [run] = useTimeoutFn(() => setDebouncedValue(value), ms);
useEffect(run, [run]);
return debouncedValue;
};