We want to hear from you!Take our 2021 Community Survey!
Support Ukraine ๐Ÿ‡บ๐Ÿ‡ฆ Help Provide Humanitarian Aid to Ukraine.

Hooks API Reference

Hook๋Š” React 16.8์—์„œ ์ƒˆ๋กœ ์ถ”๊ฐ€๋œ ๊ฐœ๋…์ž…๋‹ˆ๋‹ค. Hook์„ ํ†ตํ•ด class๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š๊ณ ๋„ state์™€ ๊ฐ™์€ React ๊ธฐ๋Šฅ๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ํŽ˜์ด์ง€๋Š” React์— ๋‚ด์žฅ๋œ Hook API๋ฅผ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

Hook์ด ์ƒ์†Œํ•˜๋‹ค๋ฉด Hook ๊ฐœ์š”๋ฅผ ๋จผ์ € ์ฝ์–ด ๋ณด๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค. ํ˜น์€ frequently asked questions์—์„œ ์œ ์šฉํ•œ ์ •๋ณด๋ฅผ ์ฐพ์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ Hook

useState

const [state, setState] = useState(initialState);

์ƒํƒœ ์œ ์ง€ ๊ฐ’๊ณผ ๊ทธ ๊ฐ’์„ ๊ฐฑ์‹ ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์ตœ์ดˆ๋กœ ๋ Œ๋”๋ง์„ ํ•˜๋Š” ๋™์•ˆ, ๋ฐ˜ํ™˜๋œ state(state)๋Š” ์ฒซ ๋ฒˆ์งธ ์ „๋‹ฌ๋œ ์ธ์ž(initialState)์˜ ๊ฐ’๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

setState ํ•จ์ˆ˜๋Š” state๋ฅผ ๊ฐฑ์‹ ํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ƒˆ state ๊ฐ’์„ ๋ฐ›์•„ ์ปดํฌ๋„ŒํŠธ ๋ฆฌ๋ Œ๋”๋ง์„ ํ์— ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

setState(newState);

๋‹ค์Œ ๋ฆฌ๋ Œ๋”๋ง ์‹œ์— useState๋ฅผ ํ†ตํ•ด ๋ฐ˜ํ™˜๋ฐ›์€ ์ฒซ ๋ฒˆ์งธ ๊ฐ’์€ ํ•ญ์ƒ ๊ฐฑ์‹ ๋œ ์ตœ์‹  state๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

์ฃผ์˜

React๋Š” setState ํ•จ์ˆ˜ ๋™์ผ์„ฑ์ด ์•ˆ์ •์ ์ด๊ณ  ๋ฆฌ๋ Œ๋”๋ง ์‹œ์—๋„ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์„ ๊ฒƒ์ด๋ผ๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด useEffect๋‚˜ useCallback ์˜์กด์„ฑ ๋ชฉ๋ก์— ์ด ํ•จ์ˆ˜๋ฅผ ํฌํ•จํ•˜์ง€ ์•Š์•„๋„ ๋ฌด๋ฐฉํ•œ ์ด์œ ์ž…๋‹ˆ๋‹ค.

ํ•จ์ˆ˜์  ๊ฐฑ์‹ 

์ด์ „ state๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ƒˆ๋กœ์šด state๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ๊ฒฝ์šฐ ํ•จ์ˆ˜๋ฅผ setState ๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ํ•จ์ˆ˜๋Š” ์ด์ „ ๊ฐ’์„ ๋ฐ›์•„ ๊ฐฑ์‹ ๋œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์— setState์˜ ์–‘์ชฝ ํ˜•ํƒœ๋ฅผ ์‚ฌ์šฉํ•œ ์นด์šดํ„ฐ ์ปดํฌ๋„ŒํŠธ์˜ ์˜ˆ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

function Counter({initialCount}) {
  const [count, setCount] = useState(initialCount);
  return (
    <>
      Count: {count}
      <button onClick={() => setCount(initialCount)}>Reset</button>
      <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
      <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
    </>
  );
}

โ€+โ€œ์™€ โ€-โ€ ๋ฒ„ํŠผ์€ ํ•จ์ˆ˜ ํ˜•์‹์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๊ฐฑ์‹ ๋œ ๊ฐ’์ด ๊ฐฑ์‹ ๋˜๊ธฐ ์ด์ „์˜ ๊ฐ’์„ ๋ฐ”ํƒ•์œผ๋กœ ๊ณ„์‚ฐ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด, โ€œResetโ€ ๋ฒ„ํŠผ์€ ์นด์šดํŠธ๋ฅผ ํ•ญ์ƒ 0์œผ๋กœ ์„ค์ •ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ผ๋ฐ˜์ ์ธ ํ˜•์‹์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์—…๋ฐ์ดํŠธ ํ•จ์ˆ˜๊ฐ€ ํ˜„์žฌ ์ƒํƒœ์™€ ์ •ํ™•ํžˆ ๋™์ผํ•œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค๋ฉด ๋ฐ”๋กœ ๋’ค์— ์ผ์–ด๋‚  ๋ฆฌ๋ Œ๋”๋ง์€ ์™„์ „ํžˆ ๊ฑด๋„ˆ๋›ฐ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ฃผ์˜

ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์˜ setState ๋ฉ”์„œ๋“œ์™€๋Š” ๋‹ค๋ฅด๊ฒŒ, useState๋Š” ๊ฐฑ์‹  ๊ฐ์ฒด(update objects)๋ฅผ ์ž๋™์œผ๋กœ ํ•ฉ์น˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. ํ•จ์ˆ˜ ์—…๋ฐ์ดํ„ฐ ํผ์„ ๊ฐ์ฒด ์ „๊ฐœ ์—ฐ์‚ฐ์ž์™€ ๊ฒฐํ•ฉํ•จ์œผ๋กœ์จ ์ด ๋™์ž‘์„ ๋ณต์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const [state, setState] = useState({});
setState(prevState => {
  // Object.assign would also work
  return {...prevState, ...updatedValues};
});

๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” useReducer๊ฐ€ ์žˆ๋Š”๋ฐ ์ด๋Š” ์—ฌ๋Ÿฌ๊ฐœ์˜ ํ•˜์œ—๊ฐ’๋“ค์„ ํฌํ•จํ•œ state ๊ฐ์ฒด๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ์— ๋” ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

์ง€์—ฐ ์ดˆ๊ธฐ state

initialState ์ธ์ž๋Š” ์ดˆ๊ธฐ ๋ Œ๋”๋ง ์‹œ์— ์‚ฌ์šฉํ•˜๋Š” state์ž…๋‹ˆ๋‹ค. ๊ทธ ์ดํ›„์˜ ๋ Œ๋”๋ง ์‹œ์—๋Š” ์ด ๊ฐ’์€ ๋ฌด์‹œ๋ฉ๋‹ˆ๋‹ค. ์ดˆ๊ธฐ state๊ฐ€ ๊ณ ๋น„์šฉ ๊ณ„์‚ฐ์˜ ๊ฒฐ๊ณผ๋ผ๋ฉด, ์ดˆ๊ธฐ ๋ Œ๋”๋ง ์‹œ์—๋งŒ ์‹คํ–‰๋  ํ•จ์ˆ˜๋ฅผ ๋Œ€์‹  ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const [state, setState] = useState(() => {
  const initialState = someExpensiveComputation(props);
  return initialState;
});

state ๊ฐฑ์‹ ์˜ ์ทจ์†Œ

State Hook์„ ํ˜„์žฌ์˜ state์™€ ๋™์ผํ•œ ๊ฐ’์œผ๋กœ ๊ฐฑ์‹ ํ•˜๋Š” ๊ฒฝ์šฐ React๋Š” ์ž์‹์„ ๋ Œ๋”๋ง ํ•œ๋‹ค๊ฑฐ๋‚˜ ๋ฌด์—‡์„ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์„ ํšŒํ”ผํ•˜๊ณ  ๊ทธ ์ฒ˜๋ฆฌ๋ฅผ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค. (React๋Š” Object.is ๋น„๊ต ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.)

์‹คํ–‰์„ ํšŒํ”ผํ•˜๊ธฐ ์ „์— React์—์„œ ํŠน์ • ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ค์‹œ ๋ Œ๋”๋งํ•˜๋Š” ๊ฒƒ์ด ์—ฌ์ „ํžˆ ํ•„์š”ํ•  ์ˆ˜๋„ ์žˆ๋‹ค๋Š” ๊ฒƒ์— ์ฃผ์˜ํ•˜์„ธ์š”. React๊ฐ€ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ํŠธ๋ฆฌ์— ๊ทธ ์ด์ƒ์œผ๋กœ ใ€Œ๋” ๊นŠ๊ฒŒใ€๋Š” ๊ด€์—ฌํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๋ฏ€๋กœ ํฌ๊ฒŒ ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š์œผ์…”๋„ ๋ฉ๋‹ˆ๋‹ค๋งŒ, ๋ Œ๋”๋ง ์‹œ์— ๊ณ ๋น„์šฉ์˜ ๊ณ„์‚ฐ์„ ํ•˜๊ณ  ์žˆ๋‹ค๋ฉด useMemo๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ทธ๊ฒƒ๋“ค์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Batching of state updates

React may group several state updates into a single re-render to improve performance. Normally, this improves performance and shouldnโ€™t affect your applicationโ€™s behavior.

Before React 18, only updates inside React event handlers were batched. Starting with React 18, batching is enabled for all updates by default. Note that React makes sure that updates from several different user-initiated events โ€” for example, clicking a button twice โ€” are always processed separately and do not get batched. This prevents logical mistakes.

In the rare case that you need to force the DOM update to be applied synchronously, you may wrap it in flushSync. However, this can hurt performance so do this only where needed.

useEffect

useEffect(didUpdate);

๋ช…๋ นํ˜• ๋˜๋Š” ์–ด๋–ค effect๋ฅผ ๋ฐœ์ƒํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค.

๋ณ€ํ˜•, ๊ตฌ๋…, ํƒ€์ด๋จธ, ๋กœ๊น… ๋˜๋Š” ๋‹ค๋ฅธ ๋ถ€์ž‘์šฉ(side effects)์€ (React์˜ ๋ Œ๋”๋ง ๋‹จ๊ณ„์— ๋”ฐ๋ฅด๋ฉด) ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ์˜ ๋ณธ๋ฌธ ์•ˆ์—์„œ๋Š” ํ—ˆ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค๋ฉด ๊ทธ๊ฒƒ์€ ๋งค์šฐ ํ˜ผ๋ž€์Šค๋Ÿฌ์šด ๋ฒ„๊ทธ ๋ฐ UI์˜ ๋ถˆ์ผ์น˜๋ฅผ ์•ผ๊ธฐํ•˜๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋Œ€์‹ ์— useEffect๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”. useEffect์— ์ „๋‹ฌ๋œ ํ•จ์ˆ˜๋Š” ํ™”๋ฉด์— ๋ Œ๋”๋ง์ด ์™„๋ฃŒ๋œ ํ›„์— ์ˆ˜ํ–‰๋˜๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. React์˜ ์ˆœ์ˆ˜ํ•œ ํ•จ์ˆ˜์ ์ธ ์„ธ๊ณ„์—์„œ ๋ช…๋ น์ ์ธ ์„ธ๊ณ„๋กœ์˜ ํƒˆ์ถœ๊ตฌ๋กœ ์ƒ๊ฐํ•˜์„ธ์š”.

๊ธฐ๋ณธ์ ์œผ๋กœ ๋™์ž‘์€ ๋ชจ๋“  ๋ Œ๋”๋ง์ด ์™„๋ฃŒ๋œ ํ›„์— ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค๋งŒ, ์–ด๋–ค ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ๋งŒ ์‹คํ–‰๋˜๊ฒŒ ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

effect ์ •๋ฆฌ

effect๋Š” ์ข…์ข… ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ™”๋ฉด์—์„œ ์ œ๊ฑฐ๋  ๋•Œ ์ •๋ฆฌํ•ด์•ผ ํ•˜๋Š” ๋ฆฌ์†Œ์Šค๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๊ฐ€๋ น ๊ตฌ๋…์ด๋‚˜ ํƒ€์ด๋จธ ID์™€ ๊ฐ™์€ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ useEffect๋กœ ์ „๋‹ฌ๋œ ํ•จ์ˆ˜๋Š” ์ •๋ฆฌ(clean-up) ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๊ตฌ๋…์„ ์ƒ์„ฑํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

useEffect(() => {
  const subscription = props.source.subscribe();
  return () => {
    // Clean up the subscription
    subscription.unsubscribe();
  };
});

์ •๋ฆฌ ํ•จ์ˆ˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด UI์—์„œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ œ๊ฑฐํ•˜๊ธฐ ์ „์— ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค. ๋”๋ถˆ์–ด, ์ปดํฌ๋„ŒํŠธ๊ฐ€ (๊ทธ๋ƒฅ ์ผ๋ฐ˜์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ) ์—ฌ๋Ÿฌ ๋ฒˆ ๋ Œ๋”๋ง ๋œ๋‹ค๋ฉด ๋‹ค์Œ effect๊ฐ€ ์ˆ˜ํ–‰๋˜๊ธฐ ์ „์— ์ด์ „ effect๋Š” ์ •๋ฆฌ๋ฉ๋‹ˆ๋‹ค. ์œ„์˜ ์˜ˆ์—์„œ, ๋งค ๊ฐฑ์‹ ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๊ตฌ๋…์ด ์ƒ์„ฑ๋œ๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐฑ์‹ ๋งˆ๋‹ค ๋ถˆํ•„์š”ํ•œ ์ˆ˜ํ–‰์ด ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ํšŒํ”ผํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋‹ค์Œ ์ ˆ์„ ์ฐธ๊ณ ํ•˜์„ธ์š”.

effect ํƒ€์ด๋ฐ

componentDidMount์™€ componentDidUpdate์™€๋Š” ๋‹ค๋ฅด๊ฒŒ, useEffect๋กœ ์ „๋‹ฌ๋œ ํ•จ์ˆ˜๋Š” ์ง€์—ฐ ์ด๋ฒคํŠธ ๋™์•ˆ์— ๋ ˆ์ด์•„์›ƒ ๋ฐฐ์น˜์™€ ๊ทธ๋ฆฌ๊ธฐ๋ฅผ ์™„๋ฃŒํ•œ ํ›„ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๊ตฌ๋…์ด๋‚˜ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ๋‹ค์ˆ˜์˜ ๊ณตํ†ต์ ์ธ ๋ถ€์ž‘์šฉ์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. ์™œ๋ƒ๋ฉด ๋Œ€๋ถ€๋ถ„์˜ ์ž‘์—…์ด ๋ธŒ๋ผ์šฐ์ €์—์„œ ํ™”๋ฉด์„ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฒƒ์„ ์ฐจ๋‹จํ•ด์„œ๋Š” ์•ˆ ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๊ทธ๋ ‡์ง€๋งŒ, ๋ชจ๋“  effect๊ฐ€ ์ง€์—ฐ๋  ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์‚ฌ์šฉ์ž์—๊ฒŒ ๋…ธ์ถœ๋˜๋Š” DOM ๋ณ€๊ฒฝ์€ ์‚ฌ์šฉ์ž๊ฐ€ ๋…ธ์ถœ๋œ ๋‚ด์šฉ์˜ ๋ถˆ์ผ์น˜๋ฅผ ๊ฒฝํ—˜ํ•˜์ง€ ์•Š๋„๋ก ๋‹ค์Œ ํ™”๋ฉด์„ ๋‹ค ๊ทธ๋ฆฌ๊ธฐ ์ด์ „์— ๋™๊ธฐํ™” ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. (๊ทธ ๊ตฌ๋ถ„์ด๋ž€ ๊ฐœ๋…์ ์œผ๋กœ๋Š” ์ˆ˜๋™์  ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ์™€ ๋Šฅ๋™์  ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ์˜ ์ฐจ์ด์™€ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค) ์ด๋Ÿฐ ์ข…๋ฅ˜์˜ effect๋ฅผ ์œ„ํ•ด React๋Š” useLayoutEffect๋ผ๋Š” ์ถ”๊ฐ€์ ์ธ Hook์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ useEffect์™€ ๋™์ผํ•œ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ  ๊ทธ๊ฒƒ์ด ์ˆ˜ํ–‰๋  ๋•Œ์—๋งŒ ์ฐจ์ด๊ฐ€ ๋‚ฉ๋‹ˆ๋‹ค.

Additionally, starting in React 18, the function passed to useEffect will fire synchronously before layout and paint when itโ€™s the result of a discrete user input such as a click, or when itโ€™s the result of an update wrapped in flushSync. This behavior allows the result of the effect to be observed by the event system, or by the caller of flushSync.

Note

This only affects the timing of when the function passed to useEffect is called - updates scheduled inside these effects are still deferred. This is different than useLayoutEffect, which fires the function and processes the updates inside of it immediately.

Even in cases where useEffect is deferred until after the browser has painted, itโ€™s guaranteed to fire before any new renders. React will always flush a previous renderโ€™s effects before starting a new update.

์กฐ๊ฑด๋ถ€ effect ๋ฐœ์ƒ

effect์˜ ๊ธฐ๋ณธ ๋™์ž‘์€ ๋ชจ๋“  ๋ Œ๋”๋ง์„ ์™„๋ฃŒํ•œ ํ›„ effect๋ฅผ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ์˜์กด์„ฑ ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋ณ€๊ฒฝ๋œ๋‹ค๋ฉด effect๋Š” ํ•ญ์ƒ ์žฌ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์€ ์ด์ „ ์„น์…˜์˜ ๊ตฌ๋… ์˜ˆ์‹œ์™€ ๊ฐ™์ด ์ผ๋ถ€ ๊ฒฝ์šฐ์—๋Š” ๊ณผ๋„ํ•œ ์ž‘์—…์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. source props๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ์—๋งŒ ํ•„์š”ํ•œ ๊ฒƒ์ด๋ผ๋ฉด ๋งค๋ฒˆ ๊ฐฑ์‹ ํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๊ตฌ๋…์„ ์ƒ์„ฑํ•  ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” useEffect์— ๋‘ ๋ฒˆ์งธ ์ธ์ž๋ฅผ ์ „๋‹ฌํ•˜์„ธ์š”. ์ด ์ธ์ž๋Š” effect๊ฐ€ ์ข…์†๋˜์–ด ์žˆ๋Š” ๊ฐ’์˜ ๋ฐฐ์—ด์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ์ ์šฉํ•œ ์˜ˆ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

useEffect(
  () => {
    const subscription = props.source.subscribe();
    return () => {
      subscription.unsubscribe();
    };
  },
  [props.source],
);

์ž ์ด์ œ, props.source๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ์—๋งŒ ๊ตฌ๋…์ด ์žฌ์ƒ์„ฑ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ฃผ์˜

์ด ์ตœ์ ํ™”๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, ๊ฐ’์˜ ๋ฐฐ์—ด์ด ์‹œ๊ฐ„์ด ์ง€๋‚จ์— ๋”ฐ๋ผ ๋ณ€๊ฒฝ๋˜๊ณ  effect์— ์‚ฌ์šฉ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ ๋ฒ”์œ„์˜ ๋ชจ๋“  ๊ฐ’๋“ค(์˜ˆ๋ฅผ ๋“ค์–ด, props์™€ state์™€ ๊ฐ™์€ ๊ฐ’๋“ค)์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”. ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋ฉด ์—ฌ๋Ÿฌ๋ถ„์˜ ์ฝ”๋“œ๋Š” ์ด์ „ ๋ Œ๋”๋ง์—์„œ ์„ค์ •๋œ ์˜ค๋ž˜๋œ ๊ฐ’์„ ์ฐธ์กฐํ•˜๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. how to deal with functions์™€ array values change too often ํ•  ๋•Œ ๋ฌด์—‡์„ ํ•  ๊ฒƒ์ธ์ง€์— ๋Œ€ํ•ด์„œ ์กฐ๊ธˆ ๋” ์•Œ์•„๋ณด์„ธ์š”.

effect๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ  (mount๋ฅผ ํ•˜๊ฑฐ๋‚˜ unmount ํ•  ๋•Œ) ๊ทธ๊ฒƒ์„ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ๋‘ ๋ฒˆ์งธ ์ธ์ž๋กœ ๋นˆ ๋ฐฐ์—ด([])์„ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด effect๋Š” React์—๊ฒŒ props๋‚˜ state์—์„œ ๊ฐ€์ ธ์˜จ ์–ด๋–ค ๊ฐ’์—๋„ ์˜์กดํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ, ๋‹ค์‹œ ์‹คํ–‰ํ•  ํ•„์š”๊ฐ€ ์ „ํ˜€ ์—†๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๋ ค์ฃผ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์„ ํŠน๋ณ„ํ•œ ๊ฒฝ์šฐ๋กœ ๊ฐ„์ฃผํ•˜์ง€๋Š” ์•Š๊ณ , ์˜์กด์„ฑ ๊ฐ’์˜ ๋ฐฐ์—ด์ด ํ•ญ์ƒ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ์ง์ ‘์ ์œผ๋กœ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ๋ฟ์ž…๋‹ˆ๋‹ค.

๋นˆ ๋ฐฐ์—ด([])์„ ์ „๋‹ฌํ•œ๋‹ค๋ฉด effect ์•ˆ์— ์žˆ๋Š” props์™€ state๋Š” ํ•ญ์ƒ ์ดˆ๊นƒ๊ฐ’์„ ๊ฐ€์ง€๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‘ ๋ฒˆ์งธ ์ธ์ž๋กœ์จ []์„ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ด ์นœ์ˆ™ํ•œ componentDidMount์™€ componentWillUnmount์— ์˜ํ•œ ๊ฐœ๋…๊ณผ ๋น„์Šทํ•˜๊ฒŒ ๋Š๊ปด์ง€๊ฒ ์ง€๋งŒ, effect๊ฐ€ ๋„ˆ๋ฌด ์ž์ฃผ ๋ฆฌ๋ Œ๋”๋ง ๋˜๋Š” ๊ฒƒ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•œ ๋ณดํ†ต ๋” ๋‚˜์€ ํ•ด๊ฒฐ์ฑ…์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋ชจ๋‘ ๊ทธ๋ ค์งˆ ๋•Œ๊นŒ์ง€ React๋Š” useEffect์˜ ์ˆ˜ํ–‰์„ ์ง€์—ฐํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ์ž‘์—…์˜ ์ˆ˜ํ–‰์ด ๋ฌธ์ œ๊ฐ€ ๋˜์ง€๋Š” ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋งˆ์„ธ์š”.

eslint-plugin-react-hooks ํŒจํ‚ค์ง€์˜ exhaustive-deps ๊ทœ์น™์„ ์‚ฌ์šฉํ•˜๊ธฐ๋ฅผ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ์˜์กด์„ฑ์ด ๋ฐ”๋ฅด์ง€ ์•Š๊ฒŒ ์ •์˜๋˜์—ˆ๋‹ค๋ฉด ๊ทธ์— ๋Œ€ํ•ด ๊ฒฝ๊ณ ํ•˜๊ณ  ์ˆ˜์ •ํ•˜๋„๋ก ์•Œ๋ ค์ค๋‹ˆ๋‹ค.

์˜์กด์„ฑ ๊ฐ’์˜ ๋ฐฐ์—ด์€ effect ํ•จ์ˆ˜์˜ ์ธ์ž๋กœ ์ „๋‹ฌ๋˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€๋งŒ ๊ฐœ๋…์ ์œผ๋กœ๋Š”, ์ด ๊ธฐ๋ฒ•์€ effect ํ•จ์ˆ˜๊ฐ€ ๋ฌด์—‡์ผ์ง€๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. effect ํ•จ์ˆ˜ ์•ˆ์—์„œ ์ฐธ์กฐ๋˜๋Š” ๋ชจ๋“  ๊ฐ’์€ ์˜์กด์„ฑ ๊ฐ’์˜ ๋ฐฐ์—ด์— ๋“œ๋Ÿฌ๋‚˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‚˜์ค‘์—๋Š” ์ถฉ๋ถ„ํžˆ ๋ฐœ์ „๋œ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ด ๋ฐฐ์—ด์„ ์ž๋™์ ์œผ๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

useContext

const value = useContext(MyContext);

context ๊ฐ์ฒด(React.createContext์—์„œ ๋ฐ˜ํ™˜๋œ ๊ฐ’)์„ ๋ฐ›์•„ ๊ทธ context์˜ ํ˜„์žฌ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. context์˜ ํ˜„์žฌ ๊ฐ’์€ ํŠธ๋ฆฌ ์•ˆ์—์„œ ์ด Hook์„ ํ˜ธ์ถœํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์— ๊ฐ€์žฅ ๊ฐ€๊นŒ์ด์— ์žˆ๋Š” <MyContext.Provider>์˜ value prop์— ์˜ํ•ด ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค.

์ปดํฌ๋„ŒํŠธ์—์„œ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด <MyContext.Provider>๊ฐ€ ๊ฐฑ์‹ ๋˜๋ฉด ์ด Hook์€ ๊ทธ MyContext provider์—๊ฒŒ ์ „๋‹ฌ๋œ ๊ฐ€์žฅ ์ตœ์‹ ์˜ context value๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ Œ๋”๋Ÿฌ๋ฅผ ํŠธ๋ฆฌ๊ฑฐ ํ•ฉ๋‹ˆ๋‹ค. ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ์—์„œ React.memo ๋˜๋Š” shouldComponentUpdate๋ฅผ ์‚ฌ์šฉํ•˜๋”๋ผ๋„ useContext๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ ์ž์ฒด์—์„œ๋ถ€ํ„ฐ ๋‹ค์‹œ ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค.

useContext๋กœ ์ „๋‹ฌํ•œ ์ธ์ž๋Š” context ๊ฐ์ฒด ๊ทธ ์ž์ฒด์ด์–ด์•ผ ํ•จ์„ ์žŠ์ง€ ๋งˆ์„ธ์š”.

  • ๋งž๋Š” ์‚ฌ์šฉ: useContext(MyContext)
  • ํ‹€๋ฆฐ ์‚ฌ์šฉ: useContext(MyContext.Consumer)
  • ํ‹€๋ฆฐ ์‚ฌ์šฉ: useContext(MyContext.Provider)

useContext๋ฅผ ํ˜ธ์ถœํ•œ ์ปดํฌ๋„ŒํŠธ๋Š” context ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด ํ•ญ์ƒ ๋ฆฌ๋ Œ๋”๋ง ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฆฌ๋ Œ๋”๋ง ํ•˜๋Š” ๊ฒƒ์— ๋น„์šฉ์ด ๋งŽ์ด ๋“ ๋‹ค๋ฉด, ๋ฉ”๋ชจ์ด์ œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํŒ

์—ฌ๋Ÿฌ๋ถ„์ด Hook ๋ณด๋‹ค context API์— ์นœ์ˆ™ํ•˜๋‹ค๋ฉด useContext(MyContext)๋Š” ํด๋ž˜์Šค์—์„œ์˜ static contextType = MyContext ๋˜๋Š” <MyContext.Consumer>์™€ ๊ฐ™๋‹ค๊ณ  ๋ณด๋ฉด ๋ฉ๋‹ˆ๋‹ค.

useContext(MyContext)๋Š” context๋ฅผ ์ฝ๊ณ  context์˜ ๋ณ€๊ฒฝ์„ ๊ตฌ๋…ํ•˜๋Š” ๊ฒƒ๋งŒ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. context์˜ ๊ฐ’์„ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์—ฌ์ „ํžˆ ํŠธ๋ฆฌ์˜ ์œ— ๊ณ„์ธต์—์„œ์˜ <MyContext.Provider>๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

useContext๋ฅผ Context.Provider์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•ด์ฃผ์„ธ์š”

const themes = {
  light: {
    foreground: "#000000",
    background: "#eeeeee"
  },
  dark: {
    foreground: "#ffffff",
    background: "#222222"
  }
};

const ThemeContext = React.createContext(themes.light);

function App() {
  return (
    <ThemeContext.Provider value={themes.dark}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  const theme = useContext(ThemeContext);  return (    <button style={{ background: theme.background, color: theme.foreground }}>      I am styled by theme context!    </button>  );
}

ํ•ด๋‹น ์˜ˆ์‹œ๋Š” Context ๊ณ ๊ธ‰ ์•ˆ๋‚ด์„œ์—์„œ ์‚ฌ์šฉํ–ˆ๋˜ ์˜ˆ์‹œ๊ฐ€ hook์œผ๋กœ ์ˆ˜์ •๋˜์—ˆ์œผ๋ฉฐ ์•ˆ๋‚ด์„œ์—์„œ Context๋ฅผ ์–ธ์ œ, ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์ž์„ธํžˆ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ถ”๊ฐ€ Hook

๋‹ค์Œ์˜ Hook๋Š” ์ด์ „ ์„น์…˜์—์„œ์˜ ๊ธฐ๋ณธ Hook์˜ ๋ณ€๊ฒฝ์ด๊ฑฐ๋‚˜ ํŠน์ •ํ•œ ๊ฒฝ์šฐ์—๋งŒ ํ•„์š”ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ตํžˆ๋Š” ๊ฒƒ์— ๋„ˆ๋ฌด ์••๋ฐ•๋ฐ›์ง€๋Š” ๋งˆ์„ธ์š”.

useReducer

const [state, dispatch] = useReducer(reducer, initialArg, init);

useState์˜ ๋Œ€์ฒด ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. (state, action) => newState์˜ ํ˜•ํƒœ๋กœ reducer๋ฅผ ๋ฐ›๊ณ  dispatch ๋ฉ”์„œ๋“œ์™€ ์ง์˜ ํ˜•ํƒœ๋กœ ํ˜„์žฌ state๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. (Redux์— ์ต์ˆ™ํ•˜๋‹ค๋ฉด ์ด๊ฒƒ์ด ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ์—ฌ๋Ÿฌ๋ถ„์€ ์ด๋ฏธ ์•Œ๊ณ  ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.)

๋‹ค์ˆ˜์˜ ํ•˜์œ—๊ฐ’์„ ํฌํ•จํ•˜๋Š” ๋ณต์žกํ•œ ์ •์  ๋กœ์ง์„ ๋งŒ๋“œ๋Š” ๊ฒฝ์šฐ๋‚˜ ๋‹ค์Œ state๊ฐ€ ์ด์ „ state์— ์˜์กด์ ์ธ ๊ฒฝ์šฐ์— ๋ณดํ†ต useState๋ณด๋‹ค useReducer๋ฅผ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ useReducer๋Š” ์ž์„ธํ•œ ์—…๋ฐ์ดํŠธ๋ฅผ ํŠธ๋ฆฌ๊ฑฐ ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์˜ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š”๋ฐ, ์ด๊ฒƒ์€ ์ฝœ๋ฐฑ ๋Œ€์‹  dispatch๋ฅผ ์ „๋‹ฌ ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์•„๋ž˜๋Š” useState ๋‚ด์šฉ์— ์žˆ๋˜ ์นด์šดํ„ฐ ์˜ˆ์‹œ์ธ๋ฐ reducer๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋‹ค์‹œ ์ž‘์„ฑํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

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

์ฃผ์˜

React๋Š” dispatch ํ•จ์ˆ˜์˜ ๋™์ผ์„ฑ์ด ์•ˆ์ •์ ์ด๊ณ  ๋ฆฌ๋ Œ๋”๋ง ์‹œ์—๋„ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์œผ๋ฆฌ๋ผ๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด useEffect๋‚˜ useCallback ์˜์กด์„ฑ ๋ชฉ๋ก์— ์ด ํ•จ์ˆ˜๋ฅผ ํฌํ•จํ•˜์ง€ ์•Š์•„๋„ ๊ดœ์ฐฎ์€ ์ด์œ ์ž…๋‹ˆ๋‹ค.

์ดˆ๊ธฐ state์˜ ๊ตฌ์ฒดํ™”

useReducer state์˜ ์ดˆ๊ธฐํ™”์—๋Š” ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์œ ์Šค์ผ€์ด์Šค์— ๋”ฐ๋ผ์„œ ํ•œ ๊ฐ€์ง€๋ฅผ ์„ ํƒํ•˜์„ธ์š”. ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์€ ์ดˆ๊ธฐ state๋ฅผ ๋‘ ๋ฒˆ์งธ ์ธ์ž๋กœ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

  const [state, dispatch] = useReducer(
    reducer,
    {count: initialCount}  );

์ฃผ์˜

React์—์„œ๋Š” Reducer์˜ ์ธ์ž๋กœ์จ state = initialState์™€ ๊ฐ™์€ ์ดˆ๊ธฐ๊ฐ’์„ ๋‚˜ํƒ€๋‚ด๋Š”, Redux์—์„œ๋Š” ๋ณดํŽธํ™”๋œ ๊ด€์Šต์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋•Œ๋•Œ๋กœ ์ดˆ๊ธฐ๊ฐ’์€ props์— ์˜์กดํ•  ํ•„์š”๊ฐ€ ์žˆ์–ด Hook ํ˜ธ์ถœ์—์„œ ์ง€์ •๋˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. ์ดˆ๊ธฐ๊ฐ’์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฒƒ์ด ์ •๋ง ํ•„์š”ํ•˜๋‹ค๋ฉด useReducer(reducer, undefined, reducer)๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ Redux๋ฅผ ๋ชจ๋ฐฉํ•  ์ˆ˜๋Š” ์žˆ๊ฒ ์ง€๋งŒ, ์ด ๋ฐฉ๋ฒ•์„ ๊ถŒ์žฅํ•˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.

์ดˆ๊ธฐํ™” ์ง€์—ฐ

์ดˆ๊ธฐ state๋ฅผ ์กฐ๊ธˆ ์ง€์—ฐํ•ด์„œ ์ƒ์„ฑํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด์„œ๋Š” init ํ•จ์ˆ˜๋ฅผ ์„ธ ๋ฒˆ์งธ ์ธ์ž๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ์ดˆ๊ธฐ state๋Š” init(initialArg)์— ์„ค์ •๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ reducer ์™ธ๋ถ€์—์„œ ์ดˆ๊ธฐ state๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ๋กœ์ง์„ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ, ์–ด๋–ค ํ–‰๋™์— ๋Œ€ํ•œ ๋Œ€์‘์œผ๋กœ ๋‚˜์ค‘์— state๋ฅผ ์žฌ์„ค์ •ํ•˜๋Š” ๋ฐ์—๋„ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

function init(initialCount) {  return {count: initialCount};}
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    case 'reset':      return init(action.payload);    default:
      throw new Error();
  }
}

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

dispatch์˜ ํšŒํ”ผ

Reducer Hook์—์„œ ํ˜„์žฌ state์™€ ๊ฐ™์€ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒฝ์šฐ React๋Š” ์ž์‹์„ ๋ฆฌ๋ Œ๋”๋งํ•˜๊ฑฐ๋‚˜ effect๋ฅผ ๋ฐœ์ƒํ•˜์ง€ ์•Š๊ณ  ์ด๊ฒƒ๋“ค์„ ํšŒํ”ผํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. (React๋Š” Object.is ๋น„๊ต ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.)

์‹คํ–‰์„ ํšŒํ”ผํ•˜๊ธฐ ์ „์— React์—์„œ ํŠน์ • ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ค์‹œ ๋ Œ๋”๋งํ•˜๋Š” ๊ฒƒ์ด ์—ฌ์ „ํžˆ ํ•„์š”ํ•  ์ˆ˜๋„ ์žˆ๋‹ค๋Š” ๊ฒƒ์— ์ฃผ์˜ํ•˜์„ธ์š”. React๊ฐ€ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ํŠธ๋ฆฌ์— ๊ทธ ์ด์ƒ์œผ๋กœ ใ€Œ๋” ๊นŠ๊ฒŒใ€ ๊นŒ์ง€๋Š” ๊ฐ€์ง€ ์•Š์„ ๊ฒƒ์ด๋ฏ€๋กœ ํฌ๊ฒŒ ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š์œผ์…”๋„ ๋ฉ๋‹ˆ๋‹ค. ๋ Œ๋”๋ง ์‹œ์— ๊ณ ๋น„์šฉ์˜ ๊ณ„์‚ฐ์„ ํ•˜๊ณ  ์žˆ๋‹ค๋ฉด useMemo๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ทธ๊ฒƒ๋“ค์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

useCallback

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

๋ฉ”๋ชจ์ด์ œ์ด์…˜๋œ ์ฝœ๋ฐฑ์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์ธ๋ผ์ธ ์ฝœ๋ฐฑ๊ณผ ๊ทธ๊ฒƒ์˜ ์˜์กด์„ฑ ๊ฐ’์˜ ๋ฐฐ์—ด์„ ์ „๋‹ฌํ•˜์„ธ์š”. useCallback์€ ์ฝœ๋ฐฑ์˜ ๋ฉ”๋ชจ์ด์ œ์ด์…˜๋œ ๋ฒ„์ „์„ ๋ฐ˜ํ™˜ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ ๋ฉ”๋ชจ์ด์ œ์ด์…˜๋œ ๋ฒ„์ „์€ ์ฝœ๋ฐฑ์˜ ์˜์กด์„ฑ์ด ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ์—๋งŒ ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€, ๋ถˆํ•„์š”ํ•œ ๋ Œ๋”๋ง์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด (์˜ˆ๋กœ shouldComponentUpdate๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ) ์ฐธ์กฐ์˜ ๋™์ผ์„ฑ์— ์˜์กด์ ์ธ ์ตœ์ ํ™”๋œ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์— ์ฝœ๋ฐฑ์„ ์ „๋‹ฌํ•  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

useCallback(fn, deps)์€ useMemo(() => fn, deps)์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ฃผ์˜

์˜์กด์„ฑ ๊ฐ’์˜ ๋ฐฐ์—ด์ด ์ฝœ๋ฐฑ์— ์ธ์ž๋กœ ์ „๋‹ฌ๋˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€๋งŒ ๊ฐœ๋…์ ์œผ๋กœ๋Š”, ์ด ๊ธฐ๋ฒ•์€ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๊ฐ€ ๋ฌด์—‡์ผ์ง€๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ์ฝœ๋ฐฑ ์•ˆ์—์„œ ์ฐธ์กฐ๋˜๋Š” ๋ชจ๋“  ๊ฐ’์€ ์˜์กด์„ฑ ๊ฐ’์˜ ๋ฐฐ์—ด์— ๋‚˜ํƒ€๋‚˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‚˜์ค‘์—๋Š” ์ถฉ๋ถ„ํžˆ ๋ฐœ์ „๋œ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ด ๋ฐฐ์—ด์„ ์ž๋™์ ์œผ๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

eslint-plugin-react-hooks ํŒจํ‚ค์ง€์˜ ์ผ๋ถ€๋กœ์จ exhaustive-deps ๊ทœ์น™์„ ์‚ฌ์šฉํ•˜๊ธฐ๋ฅผ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ์˜์กด์„ฑ์ด ๋ฐ”๋ฅด์ง€ ์•Š๊ฒŒ ์ •์˜๋˜์—ˆ๋‹ค๋ฉด ๊ทธ์— ๋Œ€ํ•ด ๊ฒฝ๊ณ ํ•˜๊ณ  ์ˆ˜์ •ํ•˜๋„๋ก ์•Œ๋ ค์ค๋‹ˆ๋‹ค.

useMemo

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

๋ฉ”๋ชจ์ด์ œ์ด์…˜๋œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

โ€œ์ƒ์„ฑ(create)โ€ ํ•จ์ˆ˜์™€ ๊ทธ๊ฒƒ์˜ ์˜์กด์„ฑ ๊ฐ’์˜ ๋ฐฐ์—ด์„ ์ „๋‹ฌํ•˜์„ธ์š”. useMemo๋Š” ์˜์กด์„ฑ์ด ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ์—๋งŒ ๋ฉ”๋ชจ์ด์ œ์ด์…˜๋œ ๊ฐ’๋งŒ ๋‹ค์‹œ ๊ณ„์‚ฐ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ์ตœ์ ํ™”๋Š” ๋ชจ๋“  ๋ Œ๋”๋ง ์‹œ์˜ ๊ณ ๋น„์šฉ ๊ณ„์‚ฐ์„ ๋ฐฉ์ง€ํ•˜๊ฒŒ ํ•ด ์ค๋‹ˆ๋‹ค.

useMemo๋กœ ์ „๋‹ฌ๋œ ํ•จ์ˆ˜๋Š” ๋ Œ๋”๋ง ์ค‘์— ์‹คํ–‰๋œ๋‹ค๋Š” ๊ฒƒ์„ ๊ธฐ์–ตํ•˜์„ธ์š”. ํ†ต์ƒ์ ์œผ๋กœ ๋ Œ๋”๋ง ์ค‘์—๋Š” ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ์ด ํ•จ์ˆ˜ ๋‚ด์—์„œ ํ•˜์ง€ ๋งˆ์„ธ์š”. ์˜ˆ๋ฅผ ๋“ค์–ด, ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ(side effects)๋Š” useEffect์—์„œ ํ•˜๋Š” ์ผ์ด์ง€ useMemo์—์„œ ํ•˜๋Š” ์ผ์ด ์•„๋‹™๋‹ˆ๋‹ค.

๋ฐฐ์—ด์ด ์—†๋Š” ๊ฒฝ์šฐ ๋งค ๋ Œ๋”๋ง ๋•Œ๋งˆ๋‹ค ์ƒˆ ๊ฐ’์„ ๊ณ„์‚ฐํ•˜๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

useMemo๋Š” ์„ฑ๋Šฅ ์ตœ์ ํ™”๋ฅผ ์œ„ํ•ด ์‚ฌ์šฉํ•  ์ˆ˜๋Š” ์žˆ์ง€๋งŒ ์˜๋ฏธ์ƒ์œผ๋กœ ๋ณด์žฅ์ด ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€๋Š” ๋งˆ์„ธ์š”. ๊ฐ€๊นŒ์šด ๋ฏธ๋ž˜์— React์—์„œ๋Š”, ์ด์ „ ๋ฉ”๋ชจ์ด์ œ์ด์…˜๋œ ๊ฐ’๋“ค์˜ ์ผ๋ถ€๋ฅผ โ€œ์žŠ์–ด๋ฒ„๋ฆฌ๊ณ โ€ ๋‹ค์Œ ๋ Œ๋”๋ง ์‹œ์— ๊ทธ๊ฒƒ๋“ค์„ ์žฌ๊ณ„์‚ฐํ•˜๋Š” ๋ฐฉํ–ฅ์„ ํƒํ• ์ง€๋„ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค๋ฉด, ์˜คํ”„์Šคํฌ๋ฆฐ ์ปดํฌ๋„ŒํŠธ์˜ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ•ด์ œํ•˜๋Š” ๋“ฑ์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. useMemo๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ ๋„ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ๊ทธ๊ฒƒ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•˜์„ธ์š”.

์ฃผ์˜

์˜์กด์„ฑ ๊ฐ’์˜ ๋ฐฐ์—ด์€ ํ•จ์ˆ˜์— ์ธ์ž๋กœ ์ „๋‹ฌ๋˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€๋งŒ ๊ฐœ๋…์ ์œผ๋กœ๋Š”, ์ด ๊ธฐ๋ฒ•์€ ํ•จ์ˆ˜๊ฐ€ ๋ฌด์—‡์ผ์ง€๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ํ•จ์ˆ˜ ์•ˆ์—์„œ ์ฐธ์กฐ๋˜๋Š” ๋ชจ๋“  ๊ฐ’์€ ์˜์กด์„ฑ ๊ฐ’์˜ ๋ฐฐ์—ด์— ๋‚˜ํƒ€๋‚˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‚˜์ค‘์—๋Š” ์ถฉ๋ถ„ํžˆ ๋ฐœ์ „๋œ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ด ๋ฐฐ์—ด์„ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

eslint-plugin-react-hooks ํŒจํ‚ค์ง€์˜ ์ผ๋ถ€๋กœ์จ exhaustive-deps ๊ทœ์น™์„ ์‚ฌ์šฉํ•˜๊ธฐ๋ฅผ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ์˜์กด์„ฑ์ด ๋ฐ”๋ฅด์ง€ ์•Š๊ฒŒ ์ •์˜๋˜์—ˆ๋‹ค๋ฉด ๊ทธ์— ๋Œ€ํ•ด ๊ฒฝ๊ณ ํ•˜๊ณ  ์ˆ˜์ •ํ•˜๋„๋ก ์•Œ๋ ค์ค๋‹ˆ๋‹ค.

useRef

const refContainer = useRef(initialValue);

useRef๋Š” .current ํ”„๋กœํผํ‹ฐ๋กœ ์ „๋‹ฌ๋œ ์ธ์ž(initialValue)๋กœ ์ดˆ๊ธฐํ™”๋œ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•œ ref ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜ํ™˜๋œ ๊ฐ์ฒด๋Š” ์ปดํฌ๋„ŒํŠธ์˜ ์ „ ์ƒ์• ์ฃผ๊ธฐ๋ฅผ ํ†ตํ•ด ์œ ์ง€๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ผ๋ฐ˜์ ์ธ ์œ ์Šค์ผ€์ด์Šค๋Š” ์ž์‹์—๊ฒŒ ๋ช…๋ น์ ์œผ๋กœ ์ ‘๊ทผํ•˜๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค.

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

๋ณธ์งˆ์ ์œผ๋กœ useRef๋Š” .current ํ”„๋กœํผํ‹ฐ์— ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•œ ๊ฐ’์„ ๋‹ด๊ณ  ์žˆ๋Š” โ€œ์ƒ์žโ€์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์•„๋งˆ๋„ ์—ฌ๋Ÿฌ๋ถ„์€ DOM์— ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ refs์— ์นœ์ˆ™ํ•  ์ง€๋„ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. <div ref={myRef} />๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ React๋กœ ref ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•œ๋‹ค๋ฉด, React๋Š” ๋…ธ๋“œ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ๋ณ€๊ฒฝ๋œ DOM ๋…ธ๋“œ์— ๊ทธ๊ฒƒ์˜ .current ํ”„๋กœํผํ‹ฐ๋ฅผ ์„ค์ •ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ทธ๋ ‡์ง€๋งŒ, ref ์†์„ฑ๋ณด๋‹ค useRef()๊ฐ€ ๋” ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์€ ํด๋ž˜์Šค์—์„œ ์ธ์Šคํ„ด์Šค ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ ์œ ์‚ฌํ•œ ์–ด๋–ค ๊ฐ€๋ณ€๊ฐ’์„ ์œ ์ง€ํ•˜๋Š” ๋ฐ์— ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ useRef()๊ฐ€ ์ˆœ์ˆ˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. useRef()์™€ {current: ...} ๊ฐ์ฒด ์ž์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์˜ ์œ ์ผํ•œ ์ฐจ์ด์ ์ด๋ผ๋ฉด useRef๋Š” ๋งค๋ฒˆ ๋ Œ๋”๋ง์„ ํ•  ๋•Œ ๋™์ผํ•œ ref ๊ฐ์ฒด๋ฅผ ์ œ๊ณตํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

useRef๋Š” ๋‚ด์šฉ์ด ๋ณ€๊ฒฝ๋  ๋•Œ ๊ทธ๊ฒƒ์„ ์•Œ๋ ค์ฃผ์ง€๋Š” ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์„ ์œ ๋…ํ•˜์„ธ์š”. .current ํ”„๋กœํผํ‹ฐ๋ฅผ ๋ณ€ํ˜•ํ•˜๋Š” ๊ฒƒ์ด ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐœ์ƒ์‹œํ‚ค์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. React๊ฐ€ DOM ๋…ธ๋“œ์— ref๋ฅผ attachํ•˜๊ฑฐ๋‚˜ detachํ•  ๋•Œ ์–ด๋–ค ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ๋Œ€์‹  ์ฝœ๋ฐฑ ref๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.

useImperativeHandle

useImperativeHandle(ref, createHandle, [deps])

useImperativeHandle์€ ref๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์— ๋…ธ์ถœ๋˜๋Š” ์ธ์Šคํ„ด์Šค ๊ฐ’์„ ์‚ฌ์šฉ์žํ™”(customizes)ํ•ฉ๋‹ˆ๋‹ค. ํ•ญ์ƒ ๊ทธ๋ ‡๋“ฏ์ด, ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ref๋ฅผ ์‚ฌ์šฉํ•œ ๋ช…๋ นํ˜• ์ฝ”๋“œ๋Š” ํ”ผํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. useImperativeHandle๋Š” forwardRef์™€ ๋”๋ถˆ์–ด ์‚ฌ์šฉํ•˜์„ธ์š”.

function FancyInput(props, ref) {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));
  return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);

์œ„์˜ ์˜ˆ์‹œ์—์„œ <FancyInput ref={inputRef} />๋ฅผ ๋ Œ๋”๋งํ•œ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋Š” inputRef.current.focus()๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

useLayoutEffect

์ด ํ•จ์ˆ˜์˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋Š” useEffect์™€ ๋™์ผํ•˜๊ธด ํ•œ๋ฐ, ๋ชจ๋“  DOM ๋ณ€๊ฒฝ ํ›„์— ๋™๊ธฐ์ ์œผ๋กœ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ DOM์—์„œ ๋ ˆ์ด์•„์›ƒ์„ ์ฝ๊ณ  ๋™๊ธฐ์ ์œผ๋กœ ๋ฆฌ๋ Œ๋”๋งํ•˜๋Š” ๊ฒฝ์šฐ์— ์‚ฌ์šฉํ•˜์„ธ์š”. useLayoutEffect์˜ ๋‚ด๋ถ€์— ์˜ˆ์ •๋œ ๊ฐฑ์‹ ์€ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ™”๋ฉด์„ ๊ทธ๋ฆฌ๊ธฐ ์ด์ „ ์‹œ์ ์— ๋™๊ธฐ์ ์œผ๋กœ ์ˆ˜ํ–‰๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํ™”๋ฉด ๊ฐฑ์‹  ์ฐจ๋‹จ์˜ ๋ฐฉ์ง€๊ฐ€ ๊ฐ€๋Šฅํ•  ๋•Œ ํ‘œ์ค€ useEffect๋ฅผ ๋จผ์ € ์‚ฌ์šฉํ•˜์„ธ์š”.

ํŒ

ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์—์„œ ์ฝ”๋“œ๋ฅผ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒฝ์šฐ์— useLayoutEffect๋Š” componentDidMount๋‚˜ componentDidUpdate์™€ ๋™์ผํ•œ ๋‹จ๊ณ„๋ฅผ ์‹คํ–‰ํ•˜๊ฒŒ ๋œ๋‹ค๋Š” ๊ฒƒ์— ์ฃผ์˜ํ•˜๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ๋Š” ํ•˜์ง€๋งŒ, ๋จผ์ € useEffect๋ฅผ ์‚ฌ์šฉํ•ด ๋ณด๊ณ  ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค๋ฉด ๊ทธ๋‹ค์Œ์œผ๋กœ useLayoutEffect๋ฅผ ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ๋ฅผ ๊ถŒํ•ฉ๋‹ˆ๋‹ค.

์„œ๋ฒ„ ๋ Œ๋”๋ง์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋ผ๋ฉด ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋ชจ๋‘ ๋‹ค์šด๋กœ๋“œ๋  ๋•Œ๊นŒ์ง€๋Š” useLayoutEffect์™€ useEffect ์–ด๋Š ๊ฒƒ๋„ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์„ ๋ช…์‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ์„œ๋ฒ„์—์„œ ๋ Œ๋”๋ง ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ์—์„œ useLayoutEffect๊ฐ€ ์‚ฌ์šฉ๋˜๋Š” ๊ฒฝ์šฐ์— ๋Œ€ํ•ด React๊ฐ€ ๊ฒฝ๊ณ ํ•˜๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ์ˆ˜์ •ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” (์ตœ์ดˆ ๋ Œ๋”๋ง ์‹œ์— ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค๋ฉด) ๋กœ์ง์„ useEffect๋กœ ์ด๋™ํ•œ๋‹ค๊ฑฐ๋‚˜ (useLayoutEffect๊ฐ€ ์ˆ˜ํ–‰๋  ๋•Œ๊นŒ์ง€ HTML์ด ๊นจ์ ธ ๋ณด์ด๋Š” ๊ฒฝ์šฐ๋Š”) ํด๋ผ์ด์–ธํŠธ ๋ Œ๋”๋ง์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ์ปดํฌ๋„ŒํŠธ ๋…ธ์ถœ์„ ์ง€์—ฐํ•˜๋„๋ก ํ•˜์„ธ์š”.

์„œ๋ฒ„์—์„œ ๋ Œ๋”๋ง๋œ HTML์—์„œ ๋ ˆ์ด์•„์›ƒ effect๊ฐ€ ํ•„์š”ํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฐฐ์ œํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, showChild && <Child />๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์กฐ๊ฑด์ ์œผ๋กœ ๋ Œ๋”๋ง ํ•˜๊ณ  useEffect(() => { setShowChild(true); }, [])๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋…ธ์ถœ์„ ์ง€์—ฐ์‹œํ‚ค์„ธ์š”. ์ด๋Ÿฐ ๋ฐฉ๋ฒ•์œผ๋กœ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๊ฐ€ ์ฃผ์ž…๋˜๊ธฐ ์ „์— ๊นจ์ ธ ๋ณด์ผ ์ˆ˜ ์žˆ๋Š” UI๋Š” ํ‘œํ˜„๋˜์ง€ ์•Š๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

useDebugValue

useDebugValue(value)

useDebugValue๋Š” React ๊ฐœ๋ฐœ์ž๋„๊ตฌ์—์„œ ์‚ฌ์šฉ์ž Hook ๋ ˆ์ด๋ธ”์„ ํ‘œ์‹œํ•˜๋Š” ๋ฐ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, โ€œ๋‚˜๋งŒ์˜ Hook ๋งŒ๋“ค๊ธฐโ€์— ์„ค๋ช…ํ•˜๊ณ  ์žˆ๋Š” useFriendStatus ์‚ฌ์šฉ์ž Hook์— ๋Œ€ํ•ด์„œ ์ƒ๊ฐํ•ด ๋ด…์‹œ๋‹ค.

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  // ...

  // Show a label in DevTools next to this Hook  // e.g. "FriendStatus: Online"  useDebugValue(isOnline ? 'Online' : 'Offline');
  return isOnline;
}

ํŒ

๋ชจ๋“  ์‚ฌ์šฉ์ž Hook์— ๋””๋ฒ„๊ทธ ๊ฐ’์„ ์ถ”๊ฐ€ํ•˜๊ธฐ๋ฅผ ๊ถŒํ•˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์‚ฌ์šฉ์ž Hook์ด ๊ณต์œ ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์ผ๋ถ€์ผ ๋•Œ ๊ฐ€์žฅ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

๋””๋ฒ„๊ทธ ๊ฐ’ ํฌ๋งทํŒ… ์ง€์—ฐํ•˜๊ธฐ

๊ฒฝ์šฐ์— ๋”ฐ๋ผ ๋””์Šคํ”Œ๋ ˆ์ด ๊ฐ’์„ ํฌ๋งทํŒ…ํ•˜๋Š” ๊ฒƒ์ด ๊ณ ๋น„์šฉ์˜ ์—ฐ์‚ฐ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ์‚ฌ์‹ค์ƒ Hook์ด ๊ฐ์ง€๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๋ถˆํ•„์š”ํ•˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฐ ์ด์œ ๋กœ useDebugValue๋Š” ์˜ต์…˜ ๋‘ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ํฌ๋งทํŒ… ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” Hook๊ฐ€ ๊ฐ์ง€๋˜์—ˆ์„ ๋•Œ๋งŒ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ์จ ๋””๋ฒ„๊ทธ ๊ฐ’์„ ์ „๋‹ฌ๋ฐ›์•„ ํฌ๋งท๋œ ๋…ธ์ถœ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ์‚ฌ์šฉ์ž Hook์€ ๋‹ค์Œ์˜ ํฌ๋งท ํ˜•์‹์„ ์‚ฌ์šฉํ•ด์„œ toDateString ํ•จ์ˆ˜๋ฅผ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

useDebugValue(date, date => date.toDateString());

useDeferredValue

const deferredValue = useDeferredValue(value);

useDeferredValue accepts a value and returns a new copy of the value that will defer to more urgent updates. If the current render is the result of an urgent update, like user input, React will return the previous value and then render the new value after the urgent render has completed.

This hook is similar to user-space hooks which use debouncing or throttling to defer updates. The benefits to using useDeferredValue is that React will work on the update as soon as other work finishes (instead of waiting for an arbitrary amount of time), and like startTransition, deferred values can suspend without triggering an unexpected fallback for existing content.

Memoizing deferred children

useDeferredValue only defers the value that you pass to it. If you want to prevent a child component from re-rendering during an urgent update, you must also memoize that component with React.memo or React.useMemo:

function Typeahead() {
  const query = useSearchQuery('');
  const deferredQuery = useDeferredValue(query);

  // Memoizing tells React to only re-render when deferredQuery changes,
  // not when query changes.
  const suggestions = useMemo(() =>
    <SearchSuggestions query={deferredQuery} />,
    [deferredQuery]
  );

  return (
    <>
      <SearchInput query={query} />
      <Suspense fallback="Loading results...">
        {suggestions}
      </Suspense>
    </>
  );
}

Memoizing the children tells React that it only needs to re-render them when deferredQuery changes and not when query changes. This caveat is not unique to useDeferredValue, and itโ€™s the same pattern you would use with similar hooks that use debouncing or throttling.

useTransition

const [isPending, startTransition] = useTransition();

Returns a stateful value for the pending state of the transition, and a function to start it.

startTransition lets you mark updates in the provided callback as transitions:

startTransition(() => {
  setCount(count + 1);
})

isPending indicates when a transition is active to show a pending state:

function App() {
  const [isPending, startTransition] = useTransition();
  const [count, setCount] = useState(0);
  
  function handleClick() {
    startTransition(() => {
      setCount(c => c + 1);
    })
  }

  return (
    <div>
      {isPending && <Spinner />}
      <button onClick={handleClick}>{count}</button>
    </div>
  );
}

Note:

Updates in a transition yield to more urgent updates such as clicks.

Updates in a transitions will not show a fallback for re-suspended content. This allows the user to continue interacting with the current content while rendering the update.

useId

const id = useId();

useId is a hook for generating unique IDs that are stable across the server and client, while avoiding hydration mismatches.

Note

useId is not for generating keys in a list. Keys should be generated from your data.

For a basic example, pass the id directly to the elements that need it:

function Checkbox() {
  const id = useId();
  return (
    <>
      <label htmlFor={id}>Do you like React?</label>
      <input id={id} type="checkbox" name="react"/>
    </>
  );
};

For multiple IDs in the same component, append a suffix using the same id:

function NameFields() {
  const id = useId();
  return (
    <div>
      <label htmlFor={id + '-firstName'}>First Name</label>
      <div>
        <input id={id + '-firstName'} type="text" />
      </div>
      <label htmlFor={id + '-lastName'}>Last Name</label>
      <div>
        <input id={id + '-lastName'} type="text" />
      </div>
    </div>
  );
}

Note:

useId generates a string that includes the : token. This helps ensure that the token is unique, but is not supported in CSS selectors or APIs like querySelectorAll.

useId supports an identifierPrefix to prevent collisions in multi-root apps. To configure, see the options for hydrateRoot and ReactDOMServer.

Library Hooks

The following Hooks are provided for library authors to integrate libraries deeply into the React model, and are not typically used in application code.

useSyncExternalStore

const state = useSyncExternalStore(subscribe, getSnapshot[, getServerSnapshot]);

useSyncExternalStore is a hook recommended for reading and subscribing from external data sources in a way thatโ€™s compatible with concurrent rendering features like selective hydration and time slicing.

This method returns the value of the store and accepts three arguments:

  • subscribe: function to register a callback that is called whenever the store changes.
  • getSnapshot: function that returns the current value of the store.
  • getServerSnapshot: function that returns the snapshot used during server rendering.

The most basic example simply subscribes to the entire store:

const state = useSyncExternalStore(store.subscribe, store.getSnapshot);

However, you can also subscribe to a specific field:

const selectedField = useSyncExternalStore(
  store.subscribe,
  () => store.getSnapshot().selectedField,
);

When server rendering, you must serialize the store value used on the server, and provide it to useSyncExternalStore. React will use this snapshot during hydration to prevent server mismatches:

const selectedField = useSyncExternalStore(
  store.subscribe,
  () => store.getSnapshot().selectedField,
  () => INITIAL_SERVER_SNAPSHOT.selectedField,
);

Note:

getSnapshot must return a cached value. If getSnapshot is called multiple times in a row, it must return the same exact value unless there was a store update in between.

A shim is provided for supporting multiple React versions published as use-sync-external-store/shim. This shim will prefer useSyncExternalStore when available, and fallback to a user-space implementation when itโ€™s not.

As a convenience, we also provide a version of the API with automatic support for memoizing the result of getSnapshot published as use-sync-external-store/with-selector.

useInsertionEffect

useInsertionEffect(didUpdate);

The signature is identical to useEffect, but it fires synchronously before all DOM mutations. Use this to inject styles into the DOM before reading layout in useLayoutEffect. Since this hook is limited in scope, this hook does not have access to refs and cannot schedule updates.

Note:

useInsertionEffect should be limited to css-in-js library authors. Prefer useEffect or useLayoutEffect instead.

Is this page useful?Edit this page