-
Notifications
You must be signed in to change notification settings - Fork 2
Typescript Custom Hook
Junho Lee edited this page Dec 15, 2019
·
1 revision
- state 변수, 해당 변수를 변경할 수 있는 함수를 반환해줍니다.
- useState()에 인자로 초기값을 넘겨줍니다.
import React, { useState } from 'react';
function Example() {
// 새로운 state 변수를 선언하고, count라 부르겠습니다.
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
- 재사용가능한 로직을 묶어놓은 함수입니다.
- 재사용가능한 로직?
- api 호출
- 응답이 오기를 기다림
- 데이터를 받음
- 화면에 렌더링
Hook은 그냥 JavaScript 함수이지만, 두 가지 규칙을 준수해야 합니다.
-
최상위(at the top level)에서만 Hook을 호출해야 합니다. 반복문, 조건문, 중첩된 함수 내에서 Hook을 실행하지 마세요.
-
React 함수 컴포넌트 내에서만 Hook을 호출해야 합니다. 일반 JavaScript 함수에서는 Hook을 호출해서는 안 됩니다.
const useGetFeed = <T>(initData:T):[
data:T, // 데이터
setData:React.Dispatch<T>, // 데이터를 변경하는 함수
isLoading:boolean, // 현재 로딩중인지??
isError:boolean, // 에러가 났는지??
(url:string)=>void // url 을 변경시키는 함수
] => {
// 초기값 세팅
const [data, setData] = useState(initData);
const [url, setUrl] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
useEffect(() => {
const fetchData = async () => {
setIsError(false); // 에러가 없어요
setIsLoading(true); // 로딩중이에요
try {
const result = await axios(url); // 데이터 요청 기다림
if (!result.data.success) { // 실패 응답 받음
setIsLoading(false); // 로딩 끝났어요
} else {
// 성공 응답
setData(result.data.data); // 데이터 세팅
setIsLoading(false); // 로딩 끝났어요
}
} catch (e) {
setIsError(true); // 에러가 났어요
}
};
fetchData();
}, [url]); // url이 변경될 때마다 위의 함수가 실행
return [
data,
setData,
isLoading,
isError,
setUrl,
];
};
-
useGetFeed
은 재사용가능한 로직이지만 그때마다 사용하는 데이터의 타입은 다를 수 있습니다. -
string
,number
등 어떤 타입의 데이터가 필요하든지 사용 가능해야합니다. 그렇지 않으면 데이터 타입의 종류마다hooks
를 작성해야하겠죠? - 물론 타입을
any
로 주면 모든 데이터에 대응 가능하겠지만 그러면 타입스크립트를 사용하는 이유가 없기 때문에.. -
GenericType
을 이용하면 상황에 따라 다른 데이터를 다룰 수 있게 됩니다. - 밑의 예제처럼
IMusicData
타입을 갖는다라고 명시함과 동시에 호출할 수 있습니다.
// hooks 는 이렇고
const useGetFeed = <T>(initData:T):[
data:T, // 데이터
setData:React.Dispatch<T>, // 데이터를 변경하는 함수
isLoading:boolean, // 현재 로딩중인지??
isError:boolean, // 에러가 났는지??
(url:string)=>void // url 을 변경시키는 함수
]
...
// 타입은 이렇고
interface IMusicData{
id: string,
content: IMusicContent[];
comments: IComment[];
createdAt: string,
updatedAt: string,
// 생략...
}
...
const [
data,
setData,
isLoading,
isError,
setUrl
] = useGetFeed<IMusicData | null>(null);
// 사용할때는 이렇게 <IMusicData> 를 넣어줍니다.
const [] = useGetFeed<IWallpaperData | null>(null);
const [] = useGetFeed<IPhotoData | null>(null);
- 먼저 데이터를 받아오기 위해서는
setUrl
함수를 사용합니다.setUrl
은useState
로 생성한url
을 변경하는 함수입니다. -
useGetFeed
에서useEffect
는url
이 변경될 때마다 실행되기 때문에url
값에 api 주소를 넣으면 해당 주소에서 데이터를 받아오게 됩니다.
useEffect(() => {
setUrl(`${API_SERVER}/feed/workmusic/${id}`); // api call
}, []);
-
isLoading
은 이름 그대로 '현재 로딩중인지' 를 나타내는 값이기 때문에 다음과같이true
일때 로딩 아이콘을 돌려주면 되겠습니다. - 그 외에도
isError
가true
일때 다른페이지로 리다이렉트를 한다던지 필요하다면 추가적인 로직을 구현할 수 있습니다.
<S.Progress>
{isLoading && <CircularProgress color="inherit" />}
</S.Progress>
- data는 원하는 현태로 렌더링 해줍니다.
<S.FeedWrapper>
{
data.map(({
_id,
ownerId,
url,
creator,
title,
numOfComments,
views,
}) => (
<WorksCard
_id={_id}
ownerId={ownerId}
imgUrl={url}
creator={creator}
key={getShortId()}
title={title}
numOfComments={numOfComments}
views={views}
/>
))
}
</S.FeedWrapper>