๋๋์ด ๋ง์ง๋ง ์ฃผ๊ฐ ์๋ค.
์ด๋ฒ ๋ฏธ์ ์ ๋ง์ง๋ง ์ฃผ์ด์ , ์ฝ๋์ค์ฟผ๋์ ๋ง์ง๋ง ์ฃผ๋ค..
6๊ฐ์๋์ ์ด์ฌํ ๋ฌ๋ ธ๋๋ฐ ๋์ด ๋ณด์ด๋ ๊ฒ ๊ฐ์ ๋๋ฌด ์์ฝ๋ค. (๋ฌผ๋ก ๋์ ์ค๋ ฅ์ ์์ง ๊ฐ๊ธธ์ด ๋ฉ๋ค)
๊ทธ๋๋ ํ์ดํ ํ๋ฉฐ ์ด๋ฒ์ฃผ ๋ฏธ์ ์ต์ ์ ๋คํด์ ๋ผ์ฟค๊ณผ ํจ๊ป ๋๋ด๋ณผ ๊ฒ~!!!!!! ์Zใ ~~~~๐ฅ
๐TypeScript์ Recoil์ ํ์ฉํ ๋ชจ๋ฌ ์ธ๋ถ์์ญ ํด๋ฆญ์ ๋ซํ ์ฒ๋ฆฌ
์ฌ์ค airbnb์์ ํ์ด์ ๊ฐ๋จํ ๋ ๊ฑฐ๋ผ ์๊ฐํ๋๋ฐ ์๊ฐ๋ณด๋ค ํ์ ์คํฌ๋ฆฝํธ์ ๊ธธ์ ํ๋ํ๋ค.
โ ์ํ๋ ์ํฉ
+์์ด์ฝ click : SideBarDrop ์ด๋ฆผ
๋ชจ๋ฌ ์ธ ์์ญ click : SideBarDrop ๋ซํ
โ ์ค๋ฅ ์ํฉ
Type '{ ref: RefObject<HTMLDivElement>; }' is not assignable to type 'IntrinsicAttributes'.
Property 'ref' does not exist on type 'IntrinsicAttributes'. TS2322
์ผ๋จ ๊ธฐ๋ณธ์ ์ผ๋ก TS2322๋ ํ์ ์ค๋ฅ
xx is not assignable to type ์ props๋ก ๋ด๋ฆฐ ๊ฐ์ ์ฌ์ฉํ์ง ์์ ๋, ๋์ค๋ ์๋ฌ๋ผ๊ณ ํ๋ค.
ํ์ฌ ์ํฉ
const SideBar = (): JSX.Element => {
const [isDropAsignee, setIsDropAsignee] = useRecoilState(dropAsigneeState);
const dropDownElement = useRef<HTMLDivElement>(null);
const dropAsigneeHandler = () => {
setIsDropAsignee(!isDropAsignee);
};
useEffect(() => {
const dropCloseHandler = (e: MouseEvent): void => {
if (
dropDownElement.current &&
!dropDownElement.current.contains(e.target as Node)
) {
setIsDropAsignee(false);
}
};
document.addEventListener('mousedown', dropCloseHandler);
return () => {
document.removeEventListener('mousedown', dropCloseHandler);
};
}, []);
return (
<SideBarStyle>
<SideBarCell>
<SideBarTitle>
<TextGroup type={T.SMALL} content={'๋ด๋น์'} color="#6E7191" />
<CustomAddIcon onClick={() => dropAsigneeHandler()} />
<div ref={dropDownElement}>{isDropAsignee && <SideBarDrop />}</div>
</SideBarTitle>
</SideBarCell>
//์๋ต
</SideBarStyle>
)};
+์์ด์ฝ click
isDropAsignee ๋ผ๋ ๋ด๋น์ Drop์ ์ํ๋ฅผ ์์ฑํ ํ์
dropAsigneeHandler ํจ์๋ก ์ปจํธ๋กคํจ์ผ๋ก์จ ๊ฐ๋จํ๊ฒ ๋ง๋ค์๋ค.
--->๋ฌธ์ ๋ ์ธ๋ถ ์์ญ ํด๋ฆญ์ด๋ค!
๋ชจ๋ฌ ์ธ ์์ญ click
1) dropDownElement ๋ก useRef ์์ฑ = <SideBarDrop> component์ ref
2) dropCloseHandler๋ก useRef๊ฐ๊ณผ ํด๋ฆญ ๊ฐ์ ๋น๊ตํ์ฌ ๋ชจ๋ฌ ์ธ ์์ญ ํด๋ฆญ ์์๋ง ๋ซํ๋๋ก ์ค์
โ useRef์ ํ์
const element = useRef<HTMLDivElement>(null);
โ event์ type
const dropCloseHandler = (e: MouseEvent): void => {
if (
dropDownElement.current &&
!dropDownElement.current.contains(e.target as Node)
) {
setIsDropAsignee(false);
}
};
๋ค์๊ณผ ๊ฐ์ด type์ ์๋ง๊ฒ ์ค์ ํด ์ค ์ ์์์ง๋ง,
์กฐ๊ฑด๋ถ Component ์์ฒด์ ref๋ฅผ ์ง์ ํ๋ ๊ฒ์ด ๋์ง ์์๋ค.
ํ๋จ์ผ๋ก๋ Modal์ด ์์ฑ๋๊ธฐ ์ ์๋ useRef์์ฒด๊ฐ ๊ฐ์ ๊ฐ์ง๊ณ ์์ด์ผ ํ๊ธฐ ๋๋ฌธ์
ํด๋ฆญ์ด ๋์์๋๋ ๋ณด์ฌ์ง๋ SideBarDrop ์ useRef๋ฅผ ๊ฑธ์ง ์๊ณ ์๋ฆฌ๋ฅผ ๊ฐ์ง๊ณ ์์ด์ผ ํ๋ค๊ณ ํ๋จํ๋ค.
์ด๋ถ๋ถ์ ๋ํด์๋ ์ถ๊ฐ์ ์ผ๋ก ๊ณต๋ถ๊ฐ ํ์ํ ๋ฏ ํ๋ค
//์์ ์
{isDropAsignee && <SideBarDrop ref={dropDownElement}/>}
// ์์ ํ
<SideBarDropDiv ref={dropDownElement}>
{isDropAsignee && <SideBarDrop />}
</SideBarDropDiv>
๋ฐ๋ผ์ ์ด์๊ฐ์ดDiv๋ก ๊ฐ์ธ๊ณ Div์ ref ํจ์ผ๋ก์จ ํด๊ฒฐํ๋ค!
https://www.pluralsight.com/guides/using-react-refs-typescript
๐ Recoil ๊ณต๋ถํ๊ณ ์ ์ฉํ๊ธฐ
Recoil์ ๋ด๋ถ์ ์ผ๋ก ContextAPI๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๊ธฐ ๋๋ฌธ์ Recoil๋ก state๋ฅผ ์ฌ์ฉํ ์ปดํฌ๋ํธ๋ Contextet๊ณต๊ธ์ ์์ ์์ด์ผ ํ๋ค.
RecoilAPI๋ ์ฌ๋ฌ๊ฐ๋ฅผ ์ฌ์ฉํ ์๋ ์๊ณ , ์ค์ฒฉ๋ ๊ฐ๋ฅํ๋ค -> ์ด๋ RecoilAPI๋ ๊ฐ์ฅ ๊ฐ๊น์ด ์กฐ์ RecoilRoot์ ์ ๊ทผํ๋ค.
์ฐธ๊ณ (https://taegon.kim/archives/10105)
Atom:
Recoil์ ๋จ์ ๋ฐ์ดํฐ
์คํ ์ด์ ์ ์ฅ๋๊ณ ์ถ์ถ๋๋ ๋ฐ์ดํฐ๋ ๋ชจ๋ Atom์ ๊ธฐ๋ฐ์ผ๋ก ํ๋ค.
๊ณ ์ ํ key์ default(๊ธฐ๋ณธ๊ฐ)์ ๊ฐ์ง๊ณ ์๋ค.
atom์ ๊ฐ์ฒด, ๋ฐฐ์ด ๋ค ๊ฐ๋ฅํจ
export const userState = atom({
key: 'userState',
default: null,
});
export const userState = atom({
key: 'userState',
default: {
id:1
name:'tami'
}
});
์ปดํฌ๋ํธ๋ atom ๊ฐ์ฒด๋ฅผ ํ ์ ์ ๋ฌํ์ฌ ๊ฐ์ ๊ฐ์ ธ์ค๊ณ ์ค์ ํ๋ค.
useRecoilValue | useRecoilState | useSetRecoilState |
state | state, setState (useState์ ๋น์ท) | setState |
Selector
๊ฐ๊ณต๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๊ฑฐ๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ๊ณตํ์ฌ ์ ์ฅํ๊ณ ์ถ์ ๋
๊ธฐ๋ฅ์ ์คํ (๋น๋๊ธฐ ์ก์ ๊ฐ๋ฅ)
ํ์ฌ ์ํฉ์ +๋ฒํผ์ ๋๋ ์ ๋ ๋ฐ์ดํฐ type์ด ๊ตฌ๋ถ๋์ง ์์ (๋ด๋น์/ ๋ ์ด๋ธ) ๋์์ ๋ด๋ ค์ค๊ฒ ๋๋ค.
์ด๋ ํ์ฌ ๋ด๋น์์ ๋ด๋ ค์จ ์ํ (isDropAsignee)๋ก๋ง ์กฐ๊ฑด์ ์ค์์ด๊ณ ,
<SideBarDropDiv ref={dropDownElement}>
{isDropAsignee && (
<SideBarDrop type={'๋ด๋น์'} assigneeData={userList} />
)}
</SideBarDropDiv>
<SideBarDropDiv ref={dropDownElement}>
{isDropAsignee && (
<SideBarDrop type={'๋ ์ด๋ธ'} labelData={labelList} />
)}
</SideBarDropDiv>
์ด ์ธ๊ฐ์ง ์ฌ์ด๋๋ฐ์ DropDown์ ๊ด๋ฆฌํ ๋ฐฉ๋ฒ์ ํฌ๊ฒ ๋๊ฐ์ง ์ด๋ค.
1) 3๊ฐ์ง์ Dropdwon ์ํ๋ฅผ ๋ฐ๋ก ๊ด๋ฆฌํ๊ธฐ
isDropAisgnee , isDropLabel, isDropMileston
2) 3๊ฐ์ง์ Dropdwon ์ํ๋ฅผ ๊ฐ์ฒด๋ก ๊ด๋ฆฌํ๊ธฐ
atom์์ dropState๋ฅผ ๊ฐ์ฒด๋ก ๊ด๋ฆฌํ ํ
๊ฐ ํด๋ฆญ ์ selecotr๋ก dropState๋ฅผ ๋ณํ์ํค๋ ๋ฒ
--> Dropdown์ ์ํ๋ฅผ ๋ฐ๋ก ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์ ํํ๋ค.
export const dropAssigneeState = atom({
key: 'dropAssigneeState',
default: false,
});
export const dropLabelState = atom({
key: 'dropLabelState',
default: false,
});
export const dropMilestoneState = atom({
key: 'dropMilestoneState',
default: false,
});
๐ Recoil ๋น๋๊ธฐ ์ suspense
Error: IssueTable suspended while rendering, but no fallback UI was specified. Add a <Suspense fallback=...> component higher in the tree to provide a loading indicator or placeholder to display.
<React.Suspense fallback={null}>
<IssueTable />
</React.Suspense>
Recoil์ ๋ณด๋ฅ์ค์ธ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๊ธฐ ์ํด React Suspense ์ ํจ๊ป ๋์ํ๋๋ก ๋์์ธ๋์ด์๋ค๊ณ ํ๋ค. ์ปดํฌ๋ํธ๋ฅผ Suspense ๊ฒฝ๊ณ๋ก ๊ฐ์ธ์ ์์ง ๋ณด๋ฅ์ค์ธ ํ์ ํญ๋ณต๋ค์ ์ก์๋ด๊ณ ๋์ฒดํ๊ธฐ ์ํ UI๋ฅผ ๋ ๋๋ง ํ ์ ์๋ค.
ํ์ฌ๋ null๊ฐ์ ๋ฃ์ด๋์๋ค.
์ฐธ๊ณ :https://recoiljs.org/ko/docs/guides/asynchronous-data-queries/
๐ ์ง์ ๊ฐ์ฒด type์ ๋ฐ์์ฌ ๋
const IssueTableHeader = ({
IssueData,
}: {
IssueData: IssueDataProps;
}): JSX.Element => {
const IssueTableHeader = ({IssueData}: IssueDataProps}):
export interface IssueDataProps {
issueId: number;
milestoneInfo: {
description: string;
dueDate: string;
title: string;
};
title: string;
content: string;
status: string;
writer: AssigneeProps[];
createdDateTime: string;
assignees: AssigneeProps[];
labels: LabelProps[];
}
Json ํํ์ Type ์ค์
type์ ํํ๊ฐ json ๊ฐ์ฒด ํํ๋ก ๋์ด ์์ผ๋ฉด ํ์ ์ง์ ์ ๋๊ฐ์ง๋ก ์ค์ ํ ์ ์๋๋ฐ,
์์๋ก ProfileProps ๊ฐ ์ด์ ๊ฐ์ด ์๊ฒผ์ ๋ ์ฌ์ฉ ์์๋
1) ์ฌ์ฉ ์ ๊ฐ์ฒด ํ ๋น
export interface nameProps {
first: string,
last : string,
}
const ProfileComponent = ({name }: {name : ProfileProps}): JSX.Element => {
2) Type , interface ๋ํ ์ผํ๊ฒ ์ง์ ํ๊ธฐ
export interface nameProps {
name:{
first: string,
last : string,
}
}
const ProfileComponent = ({name }: ProfileProps): JSX.Element => {
์์ ๋๊ฐ ๋ ๋์ ๋ฐฉ๋ฒ์ ์ ํํ๋ค.
๐ Typescript์์ ๋ ์ง ๊ณ์ฐ ์ ์ค๋ฅ
The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. TS2362
๋ ์ง๊ณ์ฐ์ ํ ๋ ๋ ์ง๋ผ๋ฆฌ ์ฐ์ฐํ ๋ ํ์ ์คํฌ๋ฆฝํธ์์๋ ๋ช ์๋ฅผ ํด์ค์ผ ํ๋ค.
http://ccambo.github.io/Dev/Typescript/1.typescript-problem-solving-and-tips/
๊ฒ์๊ธ ์์ฑ ์๊ฐ ๋ช๋ถ ์ ์ผ๋ก ๋ํ๋ด๋ ํจ์
export const getElapsedTime = (date: string): string => {
const createdTime = new Date(date);
const current = new Date();
const gapMin = Math.floor((+current - +createdTime) / 1000 / 60);
if (gapMin < 1) return '๋ฐฉ๊ธ ์ ';
if (gapMin < 60) return `${gapMin}๋ถ ์ `;
const gapHour = Math.floor(gapMin / 60);
if (gapHour < 24) return `${gapHour}์๊ฐ ์ `;
const gapDay = Math.floor(gapHour / 24);
if (gapDay < 30) return `${gapDay}์ผ ์ `;
const gapMonth = Math.floor(gapDay / 12);
if (gapMonth < 12) return `${gapMonth}๋ฌ ์ `;
return '๋ช ๋
์ ';
};
์ด๋ ๊ฒ util ํจ์์ ๋ฃ์ด ๋ช ๋ถ ์ ์ ์์ฑ๋์์ต๋๋ค.
๋๊ธ