Skip to content

typescript-cheatsheets/react-typescript-cheatsheet-kr

ย 
ย 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

13 Commits
ย 
ย 
ย 
ย 

Repository files navigation

React+TypeScript Cheatsheets ํ•œ๊ตญ์–ดํŒ ๐Ÿ‡ฐ๐Ÿ‡ท

TypeScript์— ์ž…๋ฌธํ•˜๋Š” React ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ์น˜ํŠธ์‹œํŠธ(Cheetsheets)


react + ts logo

์›น ๋‹คํ๋จผํŠธ | ์˜์–ดํŒ | ํ”„๋กœ์ ํŠธ์— ๊ธฐ์—ฌํ•˜๊ธฐ | ์งˆ๋ฌธํ•˜๊ธฐ

๐Ÿ‘‹ ๋ณธ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋Š” @ryan_kim_kr์— ์˜ํ•ด ๊ด€๋ฆฌ๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐœ๋ฐœ์ž๋‹˜์ด React์™€ ํ•จ๊ป˜ TypeScript๋ฅผ ์‚ฌ์šฉํ•ด๋ณด๊ณ ์ž ํ•˜์‹œ๋‹ค๋‹ˆ ์ •๋ง ๊ธฐ์œ ์†Œ์‹์ด๊ตฐ์š”! ์ž˜๋ชป๋œ ๋ถ€๋ถ„์ด ๋ฐœ๊ฒฌ๋˜์–ด ์ˆ˜์ •์ด ํ•„์š”ํ•˜๊ฑฐ๋‚˜ ๋ˆ„๋ฝ๋œ ๋ถ€๋ถ„์ด ์žˆ๋‹ค๋ฉด ๊ฐœ์„  ๋˜์–ด์•ผ ํ•  ์‚ฌํ•ญ์„ ์ด์Šˆ ๋“ฑ๋กํ•ด ์ฃผ์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค. ๐Ÿ‘


All Contributors

All React + TypeScript Cheatsheets

  • ๊ธฐ์ดˆ ์น˜ํŠธ์‹œํŠธ(The Basic Cheatsheet)๋Š” React ๊ฐœ๋ฐœ์ž๊ฐ€ React app์—์„œ TS ์‚ฌ์šฉ์„ ์‹œ์ž‘ํ•˜๋Š” ๊ฒƒ์— ๋„์›€์„ ์ฃผ๊ธฐ ์œ„ํ•œ ๋‚ด์šฉ์ด ์ฃผ๋ฅผ ์ด๋ฃน๋‹ˆ๋‹ค.
    • ๋ชจ๋ฒ” ์‚ฌ๋ก€(Best Practices)๋ผ๊ณ  ์—ฌ๊ฒจ์ง€๋Š”, ๋ณต์‚ฌ + ๋ถ™์—ฌ๋„ฃ๊ธฐ ๊ฐ€๋Šฅํ•œ ์˜ˆ์‹œ
    • ๊ธฐ๋ณธ์ ์ธ TS Types ์‚ฌ์šฉ๋ฒ•๊ณผ ์„ค์ • ๋ฐฉ๋ฒ•
    • ์ž์ฃผ ๋ฌป๋Š” ์งˆ๋ฌธ(FAQ)์— ๋Œ€ํ•œ ๋‹ต๋ณ€
    • Generic type logic์€ ๊นŠ์ด ๋‹ค๋ฃจ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ ๋Œ€์‹ , ์ดˆ์‹ฌ์ž๋“ค์„ ์œ„ํ•ด ๊ฐ„๋‹จํ•œ ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ๊ธฐ์ˆ ๋“ค์„ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.
    • ๊ธฐ์ดˆ ์น˜ํŠธ์‹œํŠธ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ TypeScript์— ๋Œ€ํ•ด ๋„ˆ๋ฌด ๋งŽ์€ ๊ณต๋ถ€๋ฅผ ํ•˜์ง€ ์•Š๊ณ ์„œ๋„ ์‹œ๊ฐ„ ํšจ์œจ์ ์œผ๋กœ React ๊ฐœ๋ฐœ์— TypeScript๋ฅผ ๋น ๋ฅด๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋•๋Š” ๋ฐ ๊ทธ ๋ชฉ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ณ ๊ธ‰ ์น˜ํŠธ์‹œํŠธ(The Advanced Cheatsheet)๋Š” ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ type utilities/functions/render prop/higher order copmonents ๋˜๋Š” TS+React ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๊ณ ์ž ํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•ด generic types์˜ ๊ณ ๊ธ‰ ์‚ฌ์šฉ๋ฒ•์— ๋Œ€ํ•œ ์ดํ•ด๋ฅผ ๋•์Šต๋‹ˆ๋‹ค.
    • ์ „๋ฌธ์ ์ธ ๊ฐœ๋ฐœ์ž๋“ค์„ ์œ„ํ•œ ๋‹ค์–‘ํ•œ ํŒ๊ณผ ์š”๋ น๋“ค์„ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.
    • DefinitelyTyped์— ๊ธฐ์—ฌํ•˜๊ธฐ ์œ„ํ•œ ์กฐ์–ธ์„ ๋“œ๋ฆฝ๋‹ˆ๋‹ค.
    • ๊ณ ๊ธ‰ ์น˜ํŠธ์‹œํŠธ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ TypeScript๋ฅผ ์ตœ๋Œ€ํ•œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋•๋Š” ๋ฐ ๊ทธ ๋ชฉ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋งˆ์ด๊ทธ๋ ˆ์ดํŒ… ์น˜ํŠธ์‹œํŠธ(The Migrating Cheatsheet)๋Š” ๋Œ€๊ทœ๋ชจ ์ฝ”๋“œ๋ฒ ์ด์Šค๋ฅผ JS ๋˜๋Š” Flow์—์„œ TypsScript๋กœ ์ ์ง„์ ์œผ๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ๊ฒฝํ—˜์ž์˜ ์กฐ์–ธ์„ ์–ป๋Š”๋ฐ ๋„์›€์„ ์ค๋‹ˆ๋‹ค.
    • ์šฐ๋ฆฌ๋Š” ์—ฌ๋Ÿฌ๋ถ„์ด ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ํ•˜๋„๋ก ์„ค๋“ํ•˜๋ ค๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ฉฐ, ์ด๋ฏธ ๊ทธ๋ ‡๊ฒŒ ํ•˜๊ณ ์ž ๊ฒฐ์ •ํ•œ ์‚ฌ๋žŒ๋“ค์„ ๋•๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.
    • โš ๏ธ ์ด ์น˜ํŠธ์‹œํŠธ๋Š” ์ƒˆ๋กญ๊ฒŒ ๋งŒ๋“ค์–ด์ง„ ์น˜ํŠธ์‹œํŠธ ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋„์›€์„ ์ฃผ๊ณ ์ž ํ•˜๋Š” ๋ชจ๋“  ๋ถ„๋“ค์„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค.
  • HOC ์น˜ํŠธ์‹œํŠธ(The HOC Cheatsheet)๋Š” ์˜ˆ์‹œ์™€ ํ•จ๊ป˜ HOC๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๋ ค์ค๋‹ˆ๋‹ค.
    • Generics์— ๋Œ€ํ•œ ์ดํ•ด๊ฐ€ ์„ ํ–‰๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    • โš ๏ธ ์ด ์น˜ํŠธ์‹œํŠธ๋Š” ์ƒˆ๋กญ๊ฒŒ ๋งŒ๋“ค์–ด์ง„ ์น˜ํŠธ์‹œํŠธ ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋„์›€์„ ์ฃผ๊ณ ์ž ํ•˜๋Š” ๋ชจ๋“  ๋ถ„๋“ค์„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ์ดˆ ์น˜ํŠธ์‹œํŠธ (Basic Cheatsheet)

๊ธฐ์ดˆ ์น˜ํŠธ์‹œํŠธ ๋ชฉ์ฐจ

๋ชฉ์ฐจ ํ™•์žฅํ•˜๊ธฐ

์„น์…˜ 1: React์— TypeScript ์„ค์ •ํ•˜๊ธฐ

์‹œ์ž‘ํ•˜๊ธฐ ์ „ ํ•„์š”ํ•œ ์‚ฌํ•ญ

  1. React์— ๋Œ€ํ•œ ์ถฉ๋ถ„ํ•œ ์ดํ•ด
  2. TypeScript Types์ฃผ์ œ์— ๋Œ€ํ•œ ์ดํ•ด (2ality's guide๋ฅผ ์•Œ๊ณ ์žˆ์œผ๋ฉด ๋ฌธ์„œ๋ฅผ ์ดํ•ดํ•˜๋Š”๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ TypeScript๋ฅผ ์ฒ˜์Œ ์ ‘ํ•˜๋Š” ๋ถ„์ด๋ผ๋ฉด,chibicodeโ€™s tutorial๋ฅผ ์ฐธ๊ณ ํ•ด ๋ณด์„ธ์š”.)
  3. the TypeScript section in the official React docs ์ฝ๊ธฐ
  4. the React section of the new TypeScript playground ์ฝ๊ธฐ (์„ ํƒ์‚ฌํ•ญ: the playground's Example Section์˜ 40+ examples ๋‹จ๊ณ„๋ฅผ ์ˆ˜ํ–‰ํ•ด ๋ณด๊ธฐ)

์ด ๊ฐ€์ด๋“œ๋Š” ๋…์ž๊ฐ€ ๊ฐ€์žฅ ์ตœ์‹  ๋ฒ„์ „์˜ TypeScript์™€ React๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด์ „ ๋ฒ„์ „์— ๋Œ€ํ•œ ์‚ฌํ•ญ์€ ํ™•์žฅ ๊ฐ€๋Šฅํ•œ <details> ํƒœ๊ทธ๋กœ ํ™•์ธ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

VS Code ํ™•์žฅ ํ”„๋กœ๊ทธ๋žจ(Extensions)

React + TypeScript ์ž…๋ฌธ์ž ํ‚คํŠธ

Cloud setups:

  • TypeScript Playground with React๋Š” ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ  Types๋ฅผ ๋””๋ฒ„๊น…๋งŒ ํ•˜๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • CodeSandbox - cloud IDE, ๋งค์šฐ ๋น ๋ฅธ ๋ถ€ํŒ… ์†๋„๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค.
  • Stackblitz - cloud IDE, ๋งค์šฐ ๋น ๋ฅธ ๋ถ€ํŒ… ์†๋„๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

Local dev setups:

  • Next.js: npx create-next-app -e with-typescript ๋ช…๋ น์–ด๋Š” ์ƒˆ๋กœ์šด NextJS ํ”„๋กœ์ ํŠธ๋ฅผ ํ˜„์žฌ ํด๋”์— ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • Create React App: npx create-react-app name-of-app --template typescript ๋ช…๋ น์–ด๋Š” ์ƒˆ๋กœ์šด NextJS ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒˆ๋กœ์šด ํด๋”์— ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • Vite: npm create vite@latest my-react-ts-app -- --template react-ts
  • Meteor: meteor create --typescript name-of-my-new-typescript-app
  • Ignite for React Native: ignite new myapp
  • TSDX: npx tsdx create mylib ๋ช…๋ น์–ด๋Š” React+TS ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. (in future: TurboRepo)
๋‹ค๋ฅธ ๋„๊ตฌ๋“ค

์•„์ง ๋ณด์™„์ด ํ•„์š”ํ•˜์ง€๋งŒ ํ™•์ธํ•ด ๋ณผ ๋งŒํ•œ ๊ฐ€์น˜๊ฐ€ ์žˆ๋Š” ๋„๊ตฌ๋“ค:

Manual setup:

๋น„๋””์˜ค ํŠœํ† ๋ฆฌ์–ผ

์•„๋ž˜์˜ 7๋ถ€๋กœ ๊ตฌ์„ฑ๋œ "React Typescript Course" ๋น„๋””์˜ค ์‹œ๋ฆฌ์ฆˆ๋ฅผ ํ†ตํ•ด TypeScript with React์— ๋Œ€ํ•œ ์†Œ๊ฐœ๋ฅผ ๋“ค์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

react typescript course video series

Section 2: ์‹œ์ž‘ํ•˜๊ธฐ

ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ

ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ๋Š” props๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›๊ณ  JSX element๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ผ๋ฐ˜์ ์ธ ํ•จ์ˆ˜๋กœ ์ž‘์„ฑ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// props์˜ ํƒ€์ž… ์ •์˜ - ๋” ๋งŽ์€ ์˜ˆ์‹œ๋Š” "์ปดํฌ๋„ŒํŠธ Props ํƒ€์ดํ•‘"์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
type AppProps = {
  message: string;
}; /* export ํ•œ๋‹ค๋ฉด consumer๊ฐ€ extendํ•  ์ˆ˜ ์žˆ๋„๋ก `interface`๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”. */

// ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ€์žฅ ์‰ฌ์šด ๋ฐฉ๋ฒ•; return type์€ ์ถ”๋ก ๋ฉ๋‹ˆ๋‹ค.
const App = ({ message }: AppProps) => <div>{message}</div>;

// ์‹ค์ˆ˜๋กœ ๋‹ค๋ฅธ ํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•˜์˜€์„ ๋•Œ ์—๋Ÿฌ๊ฐ€ raise ๋˜๋„๋ก return type์„ ๋ช…์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
const App = ({ message }: AppProps): JSX.Element => <div>{message}</div>;

// type ์„ ์–ธ์„ ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ ์„ ์–ธ์— ํฌํ•จ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.;์ด ๋ฐฉ๋ฒ•์€ prop types์— ์ด๋ฆ„์„ ๋ถ™์ด์ง€ ์•Š์•„๋„ ๋˜์ง€๋งŒ ์ฝ”๋“œ๊ฐ€ ๋ฐ˜๋ณต๋ฉ๋‹ˆ๋‹ค.
const App = ({ message }: { message: string }) => <div>{message}</div>;

Tip: type destructure ์„ ์–ธ์„ ์œ„ํ•ด Paul Shen's VS Code Extension๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. (keyboard shortcut์„ ์ถ”๊ฐ€ ํ•˜์„ธ์š”.)

React.FC๊ฐ€ ๊ถŒ์žฅ๋˜์ง€ ์•Š๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ผ๊นŒ์š”? React.FunctionComponent/React.VoidFunctionComponent๋Š” ์–ด๋–ค๊ฐ€์š”?

React+TypeScript codebases์—์„œ ๋‹ค์Œ ๋ณด์•˜์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const App: React.FunctionComponent<{ message: string }> = ({ message }) => <div>{message}</div>;

ํ•˜์ง€๋งŒ, ํ˜„์žฌ React.FunctionComponent (๋˜๋Š” ๊ฐ„๋žตํ•˜๊ฒŒ ์จ์„œ React.FC)๋Š” ๊ถŒ์žฅ๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์— ๋Œ€๋ถ€๋ถ„์˜ ์‚ฌ๋žŒ๋“ค์ด ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ๋ฌผ๋ก  ์ด ์ฃผ์ œ์— ๋Œ€ํ•œ ๋ฏธ๋ฌ˜ํ•œ ์˜๊ฒฌ ์ฐจ์ด๊ฐ€ ์žˆ์„ ์ˆ˜๋Š” ์žˆ์ง€๋งŒ, ๋งŒ์•ฝ ์ด ์˜๊ฒฌ์— ๋™์˜ํ•˜๊ณ  React.FC๋ฅผ ๋‹น์‹ ์˜ ์ฝ”๋“œ๋ฒ ์ด์Šค์—์„œ ์ œ๊ฑฐํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ์ด jscodeshift codemond๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

"์ผ๋ฐ˜์ ์ธ ํ•จ์ˆ˜" ๋ฒ„์ „๊ณผ์˜ ์ฐจ์ด์ ๋“ค์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • React.FunctionComponent๋Š” return type์„ ๋ช…์‹œ์ ์œผ๋กœ ๋ฐํž™๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ผ๋ฐ˜์ ์ธ ํ•จ์ˆ˜ ๋ฒ„์ „์€ ์•”์‹œ์ ์ž…๋‹ˆ๋‹ค(๋˜๋Š” ์ถ”๊ฐ€์ ์ธ ์–ด๋…ธํ…Œ์ด์…˜(annotation)์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค).

  • displayName, propTypes, ๊ทธ๋ฆฌ๊ณ  defaultProps์™€ ๊ฐ™์€ static properties๋ฅผ ์œ„ํ•œ ์ž๋™์™„์„ฑ(autocomplete)๊ณผ ํƒ€์ž… ์ฒดํฌ(Typechecking)๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

    • React.FunctionComponent์™€ ํ•จ๊ป˜ defaultProps์„ ์‚ฌ์šฉํ•˜๋Š”๋ฐ ๋ช‡ ๊ฐ€์ง€ ์•Œ๋ ค์ง„ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌธ์ œ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์„ ํ™•์ธํ•˜์„ธ์š”. ์šฐ๋ฆฌ๋Š” ๊ฐœ๋ฐœ์ž๋‹˜์ด ์ฐพ์•„๋ณผ ์ˆ˜ ์žˆ๋Š” ๋ณ„๊ฐœ์˜ defaultProps ์„น์…˜์„ ์ œ๊ณตํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  • React 18 type ์—…๋ฐ์ดํŠธ ์ด์ „์—๋Š”, React.FunctionComponent์ด children์— ๋Œ€ํ•œ ์•”์‹œ์ ์ธ ์ •์˜(implicit definition)๋ฅผ ์ œ๊ณตํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์—ด๋ค ํ† ๋ก  ๊ณผ์ •์„ ๊ฑฐ์ณค๊ณ  ๊ฒฐ๊ณผ์ ์œผ๋กœ React.FC๊ฐ€ Create React App TypeScript template์—์„œ ์ œ๊ฑฐ๋œ ์ด์œ  ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

// React 18 types ์ด์ „
const Title: React.FunctionComponent<{ title: string }> = ({ children, title }) => (
  <div title={title}>{children}</div>
);
(Deprecated)React.VoidFunctionComponent ๋˜๋Š” React.VFC ์‚ฌ์šฉํ•˜๊ธฐ

@types/react 16.9.48์—์„œ, React.VoidFunctionComponent ๋˜๋Š” React.VFC type์€ children์„ ๋ช…์‹œ์ ์œผ๋กœ ํƒ€์ดํ•‘(typing) ํ•˜๊ธฐ ์œ„ํ•ด ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, React.VFC์™€ React.VoidFunctionComponent๋Š” React 18 (DefinitelyTyped/DefinitelyTyped#59882) ์—์„œ ๋”์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค(deprecated). ๋”ฐ๋ผ์„œ ์ด ์ž„์‹œ๋ฐฉํŽธ์€ React 18+ ์—์„œ ๋”์ด์ƒ ๊ถŒ์žฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ผ๋ฐ˜์ ์ธ ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ๋‚˜ React.FC๋ฅผ ์‚ฌ์šฉํ•ด ์ฃผ์„ธ์š”.

type Props = { foo: string };

// ์ง€๊ธˆ์€ ๊ดœ์ฐฎ์ง€๋งŒ, ๋ฏธ๋ž˜์—๋Š” ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
const FunctionComponent: React.FunctionComponent<Props> = ({ foo, children }: Props) => {
  return (
    <div>
      {foo} {children}
    </div>
  ); // OK
};

// ์ง€๊ธˆ์€ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๊ณ , ๋ฏธ๋ž˜์—๋Š” ๋”์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š์„๊ฒƒ์ž…๋‹ˆ๋‹ค.(Deprecated)
const VoidFunctionComponent: React.VoidFunctionComponent<Props> = ({ foo, children }) => {
  return (
    <div>
      {foo}
      {children}
    </div>
  );
};
  • ๋ฏธ๋ž˜์—๋Š”, props๋ฅผ ์ž๋™์œผ๋กœ readonly ๋ผ๊ณ  ํ‘œ์‹œํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, props ๊ฐ์ฒด๊ฐ€ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฆฌ์ŠคํŠธ์—์„œ destructure ๋œ๋‹ค๋ฉด, ์ด๊ฒƒ์€ ์˜๋ฏธ์—†๋Š” ํ–‰๋™ ์ž…๋‹ˆ๋‹ค.

๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ์—๋Š” ์–ด๋–ค syntax๋ฅผ ์‚ฌ์šฉํ•˜๋˜์ง€ ํฐ ์ฐจ์ด๊ฐ€ ์—†์ง€๋งŒ, React.FunctionComponent์˜ ๋ณด๋‹ค ๋ช…์‹œ์ ์ธ ํŠน์„ฑ์„ ์„ ํ˜ธํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ฃผ์˜ํ•ด์•ผ ํ•  ์‚ฌํ•ญ

๋‹ค์Œ์˜ ํŒจํ„ด์€ ์ง€์›๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. :

์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋ง(conditional rendering)

const MyConditionalComponent = ({ shouldRender = false }) => (shouldRender ? <div /> : false); // JS ์—์„œ๋„ ์ด๋ ‡๊ฒŒ ํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค.
const el = <MyConditionalComponent />; // ์—๋Ÿฌ๋ฅผ throw ํ•ฉ๋‹ˆ๋‹ค.

์ด ํŒจํ„ด์ด ์ง€์›๋˜์ง€ ์•Š๋Š” ์ด์œ ๋Š” ์ปดํŒŒ์ผ๋Ÿฌ์˜ ํ•œ๊ณ„ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ๋Š” JSX expression ๋˜๋Š” null ์ด์™ธ์˜ ๋‹ค๋ฅธ ์–ด๋–ค ๊ฒƒ๋„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์—†๋Š” ๊ฒƒ์ด ๋ฐ˜ํ™˜๋œ๋‹ค๋ฉด ํ•ด๋‹น ํƒ€์ž…์€ Element์— ํ• ๋‹น๋  ์ˆ˜ ์—†๋‹ค๋Š” ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๋ฅผ ๋ณด๊ฒŒ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ("{the other type} is not assignable to Element.")

Array.fill

const MyArrayComponent = () => Array(5).fill(<div />);
const el2 = <MyArrayComponent />; // throws an error

์•„์‰ฝ๊ฒŒ๋„ ํ•จ์ˆ˜์˜ ํƒ€์ž…์„ annotate ํ•˜๋Š” ๊ฒƒ์€ ์•„๋ฌด๋Ÿฐ ๋„์›€์ด ๋˜์ง€ ์•Š์„๊ฒƒ์ž…๋‹ˆ๋‹ค. React๊ฐ€ ์ง€์›ํ•˜๋Š” ๋‹ค๋ฅธ ํŠน๋ณ„ํ•œ ํƒ€์ž…(exotic type)์„ ๋ฐ˜ํ™˜ํ•˜๊ณ ์ž ํ•œ๋‹ค๋ฉด ํƒ€์ž… ํ‘œ๋ช…(type assertion)์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. :

const MyArrayComponent = () => Array(5).fill(<div />) as any as JSX.Element;

์—ฌ๊ธฐ์„œ @ferdaber ์˜ ์„ค๋ช…์„ ํ™•์ธํ•ด๋ณด์„ธ์š”.

Hooks

Hook์€ @types/react v16.8 ์ด์ƒ๋ถ€ํ„ฐ ์ง€์›๋ฉ๋‹ˆ๋‹ค.

useState

ํƒ€์ž… ์ถ”๋ก (Type inference)์€ ๊ฐ„๋‹จํ•œ ๊ฐ’๋“ค์— ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค:

const [state, setState] = useState(false);
// `state` ๋Š” boolean ์œผ๋กœ ์ถ”๋ก ๋ฉ๋‹ˆ๋‹ค.
// `setState` ๋Š” boolean ๊ฐ’ ๋งŒ์„ ๋ฐ›์Šต๋‹ˆ๋‹ค.

ํƒ€์ž… ์ถ”๋ก ์— ๋ณต์žกํ•œ ํƒ€์ž…์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค๋ฉด ์ถ”๋ก ๋œ ํƒ€์ž…(Inferred Types) ์‚ฌ์šฉํ•˜๊ธฐ ๋„ ํ™•์ธํ•ด๋ณด์„ธ์š”.

ํ•˜์ง€๋งŒ ๋งŽ์€ hook ๋“ค์€ null ๊ฐ™์€ ๊ฐ’๋ฅผ ๋””ํดํŠธ ๊ฐ’์œผ๋กœ ์ดˆ๊ธฐํ™” ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์–ด๋–ป๊ฒŒ ํƒ€์ž…์„ ์ง€์ •ํ•˜๋Š”์ง€ ๊ถ๊ธˆํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ช…์‹œ์ ์œผ๋กœ ํƒ€์ž…์„ ์„ ์–ธํ•˜๊ณ , union type์„ ์‚ฌ์šฉํ•˜์„ธ์š”.:

const [user, setUser] = useState<User | null>(null);

// later...
setUser(newUser);

๋งŒ์•ฝ useState์„ค์ • ์งํ›„์— state๊ฐ€ ์ดˆ๊ธฐํ™”๋˜๊ณ  ๊ทธ ์ดํ›„์— ํ•ญ์ƒ ๊ฐ’์„ ๊ฐ€์ง„๋‹ค๋ฉด, ํƒ€์ž… ํ‘œ๋ช…(type assertions)์„ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

const [user, setUser] = useState<User>({} as User);

// later...
setUser(newUser);

์ด ๋ฐฉ๋ฒ•์€ ์ผ์‹œ์ ์œผ๋กœ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์ปดํŒŒ์ผ๋Ÿฌ์—๊ฒŒ {}๊ฐ€ User์˜ type์ด๋ผ๊ณ  "๊ฑฐ์ง“๋ง" ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ํ›„์— user state๋ฅผ ์„ค์ •ํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ๋‚˜๋จธ์ง€ ์ฝ”๋“œ๊ฐ€ user๋Š” User ํƒ€์ž…์ด๋ผ๋Š” ์‚ฌ์‹ค์— ์˜์กด๊ณ  ์ด๊ฒƒ์€ ๋Ÿฐํƒ€์ž… ์—๋Ÿฌ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

useReducer

Reducer actions๋ฅผ ์œ„ํ•ด Discriminated Unions๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. reducer์˜ return type์„ ์ •์˜ํ•˜๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋งˆ์„ธ์š”. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ return type์„ ์ถ”๋ก ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

import { useReducer } from "react";

const initialState = { count: 0 };

type ACTIONTYPE = { type: "increment"; payload: number } | { type: "decrement"; payload: string };

function reducer(state: typeof initialState, action: ACTIONTYPE) {
  switch (action.type) {
    case "increment":
      return { count: state.count + action.payload };
    case "decrement":
      return { count: state.count - Number(action.payload) };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: "decrement", payload: "5" })}>-</button>
      <button onClick={() => dispatch({ type: "increment", payload: 5 })}>+</button>
    </>
  );
}

TypeScript Playground์—์„œ ๋ณด๊ธฐ

Redux์—์„œ Reducer์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ธฐ

Reducer funciton์„ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•ด redux๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, return type์„ ์ฒ˜๋ฆฌํ•˜๋Š” Reducer<State, Action> ํ˜•์‹์˜ ํŽธ๋ฆฌํ•œ helper๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์œ„์˜ reducer example์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐ”๋€” ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. :

import { Reducer } from 'redux';

export function reducer: Reducer<AppState, Action>() {}

useEffect / useLayoutEffect

userEffect์™€ userLayoutEffect ๋‘˜ ๋‹ค side effect๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๊ณ  ์„ ํƒ์ ์œผ๋กœ cleanup function์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋งŒ์•ฝ ์ด hook๋“ค์ด ๋ฐ˜ํ™˜ ๊ฐ’์„ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, type์ด ํ•„์š” ์—†๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค. useEffect๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ, ํ•จ์ˆ˜ ๋˜๋Š” undefined ์ด์™ธ์˜ ๋‹ค๋ฅธ ๊ฒƒ์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๋„๋ก ์ฃผ์˜ํ•˜์„ธ์š”. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด TypeScript์™€ React๋Š” ๋‹น์‹ ์—๊ฒŒ ๋น„๋ช…์„ ์ง€๋ฅผ๊ฒƒ์ž…๋‹ˆ๋‹ค. Arros functions๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์ด ๋ฌธ์ œ๋Š” ๋‹ค์†Œ ํŒŒ์•…ํ•˜๊ธฐ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. :

function DelayedEffect(props: { timerMs: number }) {
  const { timerMs } = props;

  useEffect(
    () =>
      setTimeout(() => {
        /* do stuff */
      }, timerMs),
    [timerMs]
  );
  // ๋‚˜์œ ์˜ˆ์‹œ! setTimeout์€ ์•”๋ฌต์ ์œผ๋กœ ์ˆซ์ž๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  // arrow function์˜ body๊ฐ€ ์ค‘๊ด„ํ˜ธ๋กœ ๊ฐ์‹ธ์ง€์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
  return null;
}
์œ„ ์˜ˆ์‹œ์— ๋Œ€ํ•œ ํ•ด๊ฒฐ์ฑ…
function DelayedEffect(props: { timerMs: number }) {
  const { timerMs } = props;

  useEffect(() => {
    setTimeout(() => {
      /* do stuff */
    }, timerMs);
  }, [timerMs]);
  // ๋” ๋‚˜์€ ๋ฐฉ๋ฒ•; ํ™•์‹คํ•˜๊ฒŒ undefined๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ์œ„ํ•ด์„œ void keyword๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.
  return null;
}

useRef

TypeScript์—์„œ useRef๋Š” type argument๊ฐ€ ์ดˆ๊ธฐ ๊ฐ’์„ ์™„์ „ํžˆ ํฌํ•จ(cover)ํ•˜๋Š”์ง€ ์•„๋‹Œ์ง€์— ๋”ฐ๋ผread-only๋˜๋Š” mutable ๋‘˜ ์ค‘ ํ•˜๋‚˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ์ž์˜ use case์— ๋งž๋Š” ๊ฒƒ์„ ์„ ํƒํ•˜์„ธ์š”.

Option 1: DOM element ref

DOM element์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š”: element type ๋งŒ์„ argument๋กœ ๋„˜๊ฒจ์ฃผ๊ณ  null์„ ์ดˆ๊ธฐ ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•˜์„ธ์š”. ์ด ๊ฒฝ์šฐ์—, ๋ฐ˜ํ™˜๋˜๋Š” reference๋Š” React์— ์˜ํ•ด ๊ด€๋ฆฌ๋˜๋Š” read-only .current๋ฅผ ๊ฐ€์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค. TypeScript๋Š” ์ด ref๋ฅผ element์˜ ref prop์œผ๋กœ ์ „๋‹ฌ ๋ฐ›๊ธฐ๋ฅผ ๊ธฐ๋Œ€ํ•ฉ๋‹ˆ๋‹ค. :

function Foo() {
  // - ๊ฐ€๋Šฅํ•œ ์ƒ์„ธํ•˜๊ฒŒ ์ž‘์„ฑํ•˜์„ธ์š”. ์˜ˆ๋ฅผ๋“ค๋ฉด, HTMLDivElement๋Š” HTMLElement๋ณด๋‹ค ๋” ์ข‹๊ณ ,
  // Element๋ณด๋‹ค๋Š” ํ›จ์‹  ๋” ์ข‹์€ ์„ ํƒ์ž…๋‹ˆ๋‹ค.
  // - ๊ธฐ์ˆ ์ ์œผ๋กœ ๋งํ•˜์ž๋ฉด, ์ด๊ฒƒ์€ RefObject<HTMLDivElement>๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  const divRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    // ref.current๊ฐ€ null์ผ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์ฃผ์˜ํ•˜์„ธ์š”.
    // ์ด๊ฒƒ์€ ๋‹น์‹ ์ด ์กฐ๊ฑด์— ๋”ฐ๋ผ์„œ ref๋œ(ref-ed) element๋ฅผ renderํ•˜๊ฑฐ๋‚˜
    // ํ• ๋‹นํ•˜๋Š” ๊ฒƒ์„ ์žŠ์„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์˜ˆ์ธกํ•  ์ˆ˜ ์žˆ๋Š” ํ˜„์ƒ์ž…๋‹ˆ๋‹ค.
    if (!divRef.current) throw Error("divRef is not assigned");

    // ์ด์ œ divRef.current๋Š” ํ™•์‹คํ•˜๊ฒŒ HTMLDivElement ์ž…๋‹ˆ๋‹ค.
    doSomethingWith(divRef.current);
  });

  // React๊ฐ€ ๋‹น์‹ ์„ ์œ„ํ•ด ref๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜์žˆ๋„๋ก element์—๊ฒŒ ref๋ฅผ ์ „๋‹ฌํ•ด ์ฃผ์„ธ์š”.
  return <div ref={divRef}>etc</div>;
}

๋งŒ์•ฝ divRef.current๊ฐ€ ์ ˆ๋Œ€๋กœ null์ด ์•„๋‹๊ฒƒ์ด๋ผ๋Š” ๊ฒƒ์„ ํ™•์‹ ํ•œ๋‹ค๋ฉด, non-null assertion operator !์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. :

const divRef = useRef<HTMLDivElement>(null!);
// ๋‚˜์ค‘์—... ์ด๊ฒƒ์ด null ์ธ์ง€ ํ™•์ธํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
doSomethingWith(divRef.current);

๋‹น์‹ ์ด type safety๊ฐ€ ๋ณด์žฅ๋œ๋‹ค๊ณ  ๋ฏธ๋ฆฌ ๊ฐ€์ •ํ•˜๊ณ  ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค๋Š” ๊ฒƒ์— ์ฃผ์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ Œ๋”๋ง ๊ณผ์ •์—์„œ ref๋ฅผ element์— ํ• ๋‹นํ•˜๋Š” ๊ฒƒ์„ ์žŠ๊ฑฐ๋‚˜, ref๋œ(ref-ed) element๊ฐ€ ์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋ง ๋œ๋‹ค๋ฉด runtime error๊ฐ€ ๋ฐœ์ƒํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Tip: ์–ด๋–ค HTMLElement๋ฅผ ์‚ฌ์šฉํ• ์ง€ ์„ ํƒํ•˜๊ธฐ

Ref๋Š” ๋ช…์‹œ์„ฑ(specificity)์„ ํ•„์š”๋กœ ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, HTMLElement ๋งŒ์„ ๋ช…์‹œํ•˜๋Š” ๊ฒƒ์€ ์ถฉ๋ถ„ํ•˜์ง€ ์•Š๋‹ค๋Š” ๋ง์ž…๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋‹น์‹ ์ด ํ•„์š”ํ•œ element type์˜ ์ด๋ฆ„์„ ๋ชจ๋ฅธ๋‹ค๋ฉด, lib.dom.ts์—์„œ ํ™•์ธํ•˜๊ฑฐ๋‚˜ ์˜๋„์ ์œผ๋กœ type error๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๊ณ  language service๊ฐ€ type์˜ ์ด๋ฆ„์„ ์•Œ๋ ค์ฃผ๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

image

Option 2: Mutable value ref

mutable value๋ฅผ ๊ฐ€์ง€๊ธฐ ์œ„ํ•ด์„œ๋Š”: ์›ํ•˜๋Š” type์„ ์‚ฌ์šฉํ•˜๊ณ  ์ดˆ๊ธฐ ๊ฐ’์ด ์™„์ „ํžˆ ํ•ด๋‹น type์— ์†ํ•˜๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.

function Foo() {
  // ๊ธฐ์ˆ ์ ์œผ๋กœ ๋งํ•˜์ž๋ฉด, ์ด๊ฒƒ์€ MutableRefObject<number | null>์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  const intervalRef = useRef<number | null>(null);

  // ๋‹น์‹ ์ด ์ง์ ‘ ref๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. (์ด๊ฒƒ์ด MutableRefObject๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” ์ด์œ ์ด์ฃ .)
  useEffect(() => {
    intervalRef.current = setInterval(...);
    return () => clearInterval(intervalRef.current);
  }, []);

  // ref๋Š” element์˜ "ref" prop์œผ๋กœ ์ „๋‹ฌ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  return <button onClick={/* clearInterval the ref */}>Cancel timer</button>;
}
๋‹ค์Œ์˜ ์ž๋ฃŒ๋„ ํ™•์ธํ•ด ๋ณด์„ธ์š”.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published