Skip to content

Latest commit

ย 

History

History
604 lines (464 loc) ยท 18.6 KB

File metadata and controls

604 lines (464 loc) ยท 18.6 KB
title state ์—…๋ฐ์ดํŠธ ํ

state ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•˜๋ฉด ๋‹ค์Œ ๋ Œ๋”๋ง์ด ํ์— ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋•Œ์— ๋”ฐ๋ผ ๋‹ค์Œ ๋ Œ๋”๋ง์„ ํ์— ๋„ฃ๊ธฐ ์ „์—, ๊ฐ’์— ๋Œ€ํ•ด ์—ฌ๋Ÿฌ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์‹ถ์„ ๋•Œ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด์„œ๋Š” React๊ฐ€ state ์—…๋ฐ์ดํŠธ๋ฅผ ์–ด๋–ป๊ฒŒ ๋ฐฐ์น˜ํ•˜๋ฉด ์ข‹์„์ง€ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

  • "batching"์ด๋ž€ ๋ฌด์—‡์ด๋ฉฐ React๊ฐ€ ์—ฌ๋Ÿฌ state ์—…๋ฐ์ดํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•
  • ๋™์ผํ•œ state ๋ณ€์ˆ˜์—์„œ ์—ฌ๋Ÿฌ ์—…๋ฐ์ดํŠธ๋ฅผ ์—ฐ์†์œผ๋กœ ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

React state batches ์—…๋ฐ์ดํŠธ {/react-batches-state-updates/}

setNumber(number + 1)๋ฅผ ์„ธ ๋ฒˆ ํ˜ธ์ถœํ•˜๋ฏ€๋กœ "+3" ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ์„ธ ๋ฒˆ ์ฆ๊ฐ€ํ•  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 1);
        setNumber(number + 1);
        setNumber(number + 1);
      }}>+3</button>
    </>
  )
}
button { display: inline-block; margin: 10px; font-size: 20px; }
h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; }

์ด์ „ ์„ธ์…˜์—์„œ ๊ธฐ์–ตํ•  ์ˆ˜ ์žˆ๋“ฏ์ด ๊ฐ ๋ Œ๋”๋ง์˜ state ๊ฐ’์€ ๊ณ ์ •๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ, ์ฒซ ๋ฒˆ์งธ ๋ Œ๋”๋ง์˜ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์˜ number ๊ฐ’์€ setNumber(1)์„ ๋ช‡ ๋ฒˆ ํ˜ธ์ถœํ•˜๋“  ํ•ญ์ƒ 0์ž…๋‹ˆ๋‹ค.

setNumber(0 + 1);
setNumber(0 + 1);
setNumber(0 + 1);

ํ•˜์ง€๋งŒ ์—ฌ๊ธฐ์—๋Š” ํ•œ๊ฐ€์ง€ ์š”์ธ์ด ๋” ์žˆ์Šต๋‹ˆ๋‹ค. React๋Š” state ์—…๋ฐ์ดํŠธ๋ฅผ ํ•˜๊ธฐ ์ „์— ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์˜ ๋ชจ๋“  ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค. ์ด ๋•Œ๋ฌธ์— ๋ฆฌ๋ Œ๋”๋ง์€ ๋ชจ๋“  setNumber() ํ˜ธ์ถœ์ด ์™„๋ฃŒ๋œ ์ดํ›„์—๋งŒ ์ผ์–ด๋‚ฉ๋‹ˆ๋‹ค.

์ด๋Š” ์Œ์‹์ ์—์„œ ์ฃผ๋ฌธ๋ฐ›๋Š” ์›จ์ดํ„ฐ๋ฅผ ์ƒ๊ฐํ•ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์›จ์ดํ„ฐ๋Š” ์ฒซ ๋ฒˆ์งธ ์š”๋ฆฌ๋ฅผ ๋งํ•˜์ž๋งˆ์ž ์ฃผ๋ฐฉ์œผ๋กœ ๋‹ฌ๋ ค๊ฐ€์ง€ ์•Š์Šต๋‹ˆ๋‹ค! ๋Œ€์‹  ์ฃผ๋ฌธ์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ธ๋‹ค๊ฐ€ ์ฃผ๋ฌธ์„ ๋ณ€๊ฒฝํ•˜๊ณ , ์‹ฌ์ง€์–ด ํ…Œ์ด๋ธ”์— ์žˆ๋Š” ๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ ์ฃผ๋ฌธ๋„ ๋ฐ›์Šต๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋„ˆ๋ฌด ๋งŽ์€ ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๊ณ ๋„ ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋‚˜์˜จ ๋‹ค์ˆ˜์˜ state ๋ณ€์ˆ˜๋ฅผ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์™€ ๊ทธ ์•ˆ์— ์žˆ๋Š” ์ฝ”๋“œ๊ฐ€ ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ UI๊ฐ€ ์—…๋ฐ์ดํŠธ๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์˜๋ฏธ์ด๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. batching๋ผ๊ณ ๋„ ํ•˜๋Š” ์ด ๋™์ž‘์€ React ์•ฑ์„ ํ›จ์”ฌ ๋น ๋ฅด๊ฒŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. ๋˜ํ•œ ์ผ๋ถ€ ๋ณ€์ˆ˜๋งŒ ์—…๋ฐ์ดํŠธ๋œ "๋ฐ˜์ฏค ์™„์„ฑ๋œ" ํ˜ผ๋ž€์Šค๋Ÿฌ์šด ๋ Œ๋”๋ง์„ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค.

React๋Š” ํด๋ฆญ๊ณผ ๊ฐ™์€ ์—ฌ๋Ÿฌ ์˜๋„์ ์ธ ์ด๋ฒคํŠธ์— ๋Œ€ํ•ด batch๋ฅผ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์œผ๋ฉฐ, ๊ฐ ํด๋ฆญ์€ ๊ฐœ๋ณ„์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค. React๋Š” ์•ˆ์ „ํ•œ ๊ฒฝ์šฐ์—๋งŒ batch๋ฅผ ์ˆ˜ํ–‰ํ•˜๋‹ˆ ์•ˆ์‹ฌํ•˜์„ธ์š”. ์˜ˆ๋ฅผ ๋“ค์–ด ์ฒซ ๋ฒˆ์งธ ๋ฒ„ํŠผ ํด๋ฆญ์œผ๋กœ ์–‘์‹์ด ๋น„ํ™œ์„ฑํ™”๋˜๋ฉด ๋‘ ๋ฒˆ์งธ ํด๋ฆญ์œผ๋กœ ์–‘์‹์ด ๋‹ค์‹œ ์ œ์ถœ๋˜์ง€ ์•Š๋„๋ก ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ๋ Œ๋”๋ง ์ „์— ๋™์ผํ•œ state ๋ณ€์ˆ˜๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ {/updating-the-same-state-multiple-times-before-the-next-render/}

ํ”ํ•œ ์‚ฌ๋ก€๋Š” ์•„๋‹ˆ์ง€๋งŒ, ๋‹ค์Œ ๋ Œ๋”๋ง ์ „์— ๋™์ผํ•œ state ๋ณ€์ˆ˜๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ์—…๋ฐ์ดํŠธ ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด setNumber(number + 1) ์™€ ๊ฐ™์€ ๋‹ค์Œ state ๊ฐ’์„ ์ „๋‹ฌํ•˜๋Š” ๋Œ€์‹ , setNumber(n => n + 1) ์™€ ๊ฐ™์ด ์ด์ „ ํ์˜ state๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‹ค์Œ state๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋‹จ์ˆœํžˆ state ๊ฐ’์„ ๋Œ€์ฒดํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ React์— "state ๊ฐ’์œผ๋กœ ๋ฌด์–ธ๊ฐ€๋ฅผ ํ•˜๋ผ"๊ณ  ์ง€์‹œํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

์ด์ œ ์นด์šดํ„ฐ๋ฅผ ์ฆ๊ฐ€์‹œ์ผœ ๋ณด์„ธ์š”.

import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(n => n + 1);
        setNumber(n => n + 1);
        setNumber(n => n + 1);
      }}>+3</button>
    </>
  )
}
button { display: inline-block; margin: 10px; font-size: 20px; }
h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; }

์—ฌ๊ธฐ์„œ n => n + 1 ์€ ์—…๋ฐ์ดํ„ฐ ํ•จ์ˆ˜(updater function)๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค. ์ด๋ฅผ state ์„ค์ •์ž ํ•จ์ˆ˜์— ์ „๋‹ฌ ํ•  ๋•Œ,

  1. React๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์˜ ๋‹ค๋ฅธ ์ฝ”๋“œ๊ฐ€ ๋ชจ๋‘ ์‹คํ–‰๋œ ํ›„์— ์ด ํ•จ์ˆ˜๊ฐ€ ์ฒ˜๋ฆฌ๋˜๋„๋ก ํ์— ๋„ฃ์Šต๋‹ˆ๋‹ค.
  2. ๋‹ค์Œ ๋ Œ๋”๋ง ์ค‘์— React๋Š” ํ๋ฅผ ์ˆœํšŒํ•˜์—ฌ ์ตœ์ข… ์—…๋ฐ์ดํŠธ๋œ state๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);

React๊ฐ€ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋™์•ˆ ์—ฌ๋Ÿฌ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. setNumber(n => n + 1): n => n + 1 ํ•จ์ˆ˜๋ฅผ ํ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
  2. setNumber(n => n + 1): n => n + 1 ํ•จ์ˆ˜๋ฅผ ํ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
  3. setNumber(n => n + 1): n => n + 1 ํ•จ์ˆ˜๋ฅผ ํ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ๋ Œ๋”๋ง ์ค‘์— useState ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด React๋Š” ํ๋ฅผ ์ˆœํšŒํ•ฉ๋‹ˆ๋‹ค. ์ด์ „ number state๋Š” 0์ด์—ˆ์œผ๋ฏ€๋กœ React๋Š” ์ด๋ฅผ ์ฒซ ๋ฒˆ์งธ ์—…๋ฐ์ดํ„ฐ ํ•จ์ˆ˜์— n ์ธ์ˆ˜๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ React๋Š” ์ด์ „ ์—…๋ฐ์ดํ„ฐ ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜ ๊ฐ’์„ ๊ฐ€์ ธ์™€์„œ ๋‹ค์Œ ์—…๋ฐ์ดํ„ฐ ํ•จ์ˆ˜์— n์œผ๋กœ ์ „๋‹ฌํ•˜๋Š” ์‹์œผ๋กœ ๋ฐ˜๋ณตํ•ฉ๋‹ˆ๋‹ค.

queued update n returns
n => n + 1 0 0 + 1 = 1
n => n + 1 1 1 + 1 = 2
n => n + 1 2 2 + 1 = 3

React๋Š” 3์„ ์ตœ์ข… ๊ฒฐ๊ณผ๋กœ ์ €์žฅํ•˜๊ณ  useState์—์„œ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ์œ„ ์˜ˆ์‹œ "+3"์„ ํด๋ฆญํ•˜๋ฉด ๊ฐ’์ด 3์”ฉ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฆ๊ฐ€ํ•˜๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค.

state๋ฅผ ๊ต์ฒดํ•œ ํ›„ ์—…๋ฐ์ดํŠธํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋˜๋‚˜์š”? {/what-happens-if-you-update-state-after-replacing-it/}

์ด ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋Š” ์–ด๋–จ๊นŒ์š”? ๋‹ค์Œ ๋ Œ๋”๋ง์—์„œ number๊ฐ€ ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”?

<button onClick={() => {
  setNumber(number + 5);
  setNumber(n => n + 1);
}}>
import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 5);
        setNumber(n => n + 1);
      }}>Increase the number</button>
    </>
  )
}
button { display: inline-block; margin: 10px; font-size: 20px; }
h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; }

์ด ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ React์— ์ง€์‹œํ•˜๋Š” ์ž‘์—…์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. setNumber(number + 5) : number๋Š” 0์ด๋ฏ€๋กœ setNumber(0 + 5)์ž…๋‹ˆ๋‹ค. React๋Š” ํ์— "5๋กœ ๋ฐ”๊พธ๊ธฐ" ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
  2. setNumber(n => n + 1) : n => n + 1๋Š” ์—…๋ฐ์ดํ„ฐ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. React๋Š” ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ํ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ๋ Œ๋”๋งํ•˜๋Š” ๋™์•ˆ React๋Š” state ํ๋ฅผ ์ˆœํšŒํ•ฉ๋‹ˆ๋‹ค.

queued update n returns
"replace with 5" 0 (unused) 5
n => n + 1 5 5 + 1 = 6

React๋Š” 6์„ ์ตœ์ข… ๊ฒฐ๊ณผ๋กœ ์ €์žฅํ•˜๊ณ  useState์—์„œ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

setState(5)๊ฐ€ ์‹ค์ œ๋กœ๋Š” setState(n => 5) ์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜์ง€๋งŒ n์ด ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์„ ๋ˆˆ์น˜์ฑ„์…จ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค!

์—…๋ฐ์ดํŠธ ํ›„ state๋ฅผ ๋ฐ”๊พธ๋ฉด ์–ด๋–ป๊ฒŒ ๋˜๋‚˜์š”? {/what-happens-if-you-replace-state-after-updating-it/}

ํ•œ ๊ฐ€์ง€ ์˜ˆ๋ฅผ ๋” ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ๋ Œ๋”๋ง์—์„œ number๊ฐ€ ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”?

<button onClick={() => {
  setNumber(number + 5);
  setNumber(n => n + 1);
  setNumber(42);
}}>
import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 5);
        setNumber(n => n + 1);
        setNumber(42);
      }}>Increase the number</button>
    </>
  )
}
button { display: inline-block; margin: 10px; font-size: 20px; }
h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; }

์ด ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋™์•ˆ React๊ฐ€ ์ด ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. setNumber(number + 5): number ๋Š” 0 ์ด๋ฏ€๋กœ setNumber(0 + 5)์ž…๋‹ˆ๋‹ค. React๋Š” "5๋กœ ๋ฐ”๊พธ๊ธฐ" ๋ฅผ ํ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
  2. setNumber(n => n + 1): n => n + 1 ๋Š” ์—…๋ฐ์ดํ„ฐ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. React๋Š” ์ด ํ•จ์ˆ˜๋ฅผ ํ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
  3. setNumber(42): React๋Š” "42๋กœ ๋ฐ”๊พธ๊ธฐ" ๋ฅผ ํ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ๋ Œ๋”๋งํ•˜๋Š” ๋™์•ˆ, React๋Š” state ํ๋ฅผ ์ˆœํšŒํ•ฉ๋‹ˆ๋‹ค.

queued update n returns
"replace with 5" 0 (unused) 5
n => n + 1 5 5 + 1 = 6
"replace with 42" 6 (unused) 42

๊ทธ๋Ÿฐ ๋‹ค์Œ React๋Š” 42๋ฅผ ์ตœ์ข… ๊ฒฐ๊ณผ๋กœ ์ €์žฅํ•˜๊ณ  useState์—์„œ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์š”์•ฝํ•˜์ž๋ฉด, setNumber state ์„ค์ •์ž ํ•จ์ˆ˜์— ์ „๋‹ฌํ•  ๋‚ด์šฉ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  • ์—…๋ฐ์ดํ„ฐ ํ•จ์ˆ˜ (์˜ˆ. n => n + 1) ๊ฐ€ ํ์— ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค.
  • ๋‹ค๋ฅธ ๊ฐ’ (์˜ˆ. ์ˆซ์ž 5) ์€ ํ์— "5๋กœ ๋ฐ”๊พธ๊ธฐ"๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉฐ, ์ด๋ฏธ ํ์— ๋Œ€๊ธฐ ์ค‘์ธ ํ•ญ๋ชฉ์€ ๋ฌด์‹œํ•ฉ๋‹ˆ๋‹ค.

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

๋ช…๋ช… ๊ทœ์น™ {/naming-conventions/}

์—…๋ฐ์ดํ„ฐ ํ•จ์ˆ˜ ์ธ์ˆ˜์˜ ์ด๋ฆ„์€ ํ•ด๋‹น state ๋ณ€์ˆ˜์˜ ์ฒซ ๊ธ€์ž๋กœ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ž…๋‹ˆ๋‹ค.

setEnabled(e => !e);
setLastName(ln => ln.reverse());
setFriendCount(fc => fc * 2);

์ข€ ๋” ์ž์„ธํ•œ ์ฝ”๋“œ๋ฅผ ์„ ํ˜ธํ•˜๋Š” ๊ฒฝ์šฐ setEnabled(enabled => !enabled)์™€ ๊ฐ™์ด ์ „์ฒด state ๋ณ€์ˆ˜ ์ด๋ฆ„์„ ๋ฐ˜๋ณตํ•˜๊ฑฐ๋‚˜, setEnabled(prevEnabled => !prevEnabled)์™€ ๊ฐ™์€ ์ ‘๋‘์‚ฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๋Š” ๊ทœ์น™์ž…๋‹ˆ๋‹ค.

  • state๋ฅผ ์„ค์ •ํ•˜๋”๋ผ๋„ ๊ธฐ์กด ๋ Œ๋”๋ง์˜ ๋ณ€์ˆ˜๋Š” ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์œผ๋ฉฐ, ๋Œ€์‹  ์ƒˆ๋กœ์šด ๋ Œ๋”๋ง์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.
  • React๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์‹คํ–‰์„ ๋งˆ์นœ ํ›„ state ์—…๋ฐ์ดํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ batching ์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.
  • ํ•˜๋‚˜์˜ ์ด๋ฒคํŠธ์—์„œ ์ผ๋ถ€ state๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ์—…๋ฐ์ดํŠธํ•˜๋ ค๋ฉด setNumber(n => n + 1) ์—…๋ฐ์ดํ„ฐ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์š”์ฒญ ์นด์šดํ„ฐ๋ฅผ ๊ณ ์ณ๋ณด์„ธ์š”. {/fix-a-request-counter/}

์‚ฌ์šฉ์ž๊ฐ€ ๋™์‹œ์— ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋ฏธ์ˆ ํ’ˆ์„ ์ฃผ๋ฌธํ•  ์ˆ˜ ์žˆ๋Š” ์˜ˆ์ˆ  ์‡ผํ•‘๋ชฐ ์•ฑ์—์„œ ์ž‘์—…ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ "Buy" ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅผ ๋•Œ๋งˆ๋‹ค "Pending" ์นด์šดํ„ฐ๊ฐ€ 1์”ฉ ์ฆ๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. 3์ดˆ ํ›„์—๋Š” "Pending" ์นด์šดํ„ฐ๊ฐ€ ๊ฐ์†Œํ•˜๊ณ  "Completed" ์นด์šดํ„ฐ๊ฐ€ ์ฆ๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ "Pending" ์นด์šดํ„ฐ๊ฐ€ ์˜๋„๋Œ€๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. "Buy"๋ฅผ ๋ˆ„๋ฅด๋ฉด -1๋กœ ๊ฐ์†Œํ•ฉ๋‹ˆ๋‹ค(๊ทธ๋Ÿด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค!). ๊ทธ๋ฆฌ๊ณ  ๋น ๋ฅด๊ฒŒ ๋‘ ๋ฒˆ ํด๋ฆญํ•˜๋ฉด ๋‘ ์นด์šดํ„ฐ๊ฐ€ ๋ชจ๋‘ ์˜ˆ์ธกํ•  ์ˆ˜ ์—†๊ฒŒ ์ž‘๋™ํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์™œ ์ด๋Ÿฐ ์ผ์ด ๋ฐœ์ƒํ• ๊นŒ์š”? ๋‘ ์นด์šดํ„ฐ๋ฅผ ๋ชจ๋‘ ์ˆ˜์ •ํ•˜์„ธ์š”.

import { useState } from 'react';

export default function RequestTracker() {
  const [pending, setPending] = useState(0);
  const [completed, setCompleted] = useState(0);

  async function handleClick() {
    setPending(pending + 1);
    await delay(3000);
    setPending(pending - 1);
    setCompleted(completed + 1);
  }

  return (
    <>
      <h3>
        Pending: {pending}
      </h3>
      <h3>
        Completed: {completed}
      </h3>
      <button onClick={handleClick}>
        Buy
      </button>
    </>
  );
}

function delay(ms) {
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  });
}

handleClick ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋‚ด๋ถ€์—์„œ Pending๊ณผ Completed์˜ ๊ฐ’์€ ํด๋ฆญ ์ด๋ฒคํŠธ ๋‹น์‹œ์˜ ๊ฐ’๊ณผ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ๋ Œ๋”๋ง์˜ ๊ฒฝ์šฐ, Pending์ด 0์ด์—ˆ์œผ๋ฏ€๋กœ setPending(pending - 1)๋Š” setPending(-1) ๋˜๋Š”๋ฐ, ์ด๋Š” ์ž˜๋ชป๋œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํด๋ฆญ ์ค‘์— ๊ฒฐ์ •๋œ ๊ตฌ์ฒด์ ์ธ ๊ฐ’์œผ๋กœ ์นด์šดํ„ฐ๋ฅผ ์„ค์ •ํ•˜๋Š” ๋Œ€์‹  ์นด์šดํ„ฐ๋ฅผ ์ฆ๊ฐ€ ๋˜๋Š” ๊ฐ์†Œํ•˜๊ณ  ์‹ถ์œผ๋ฏ€๋กœ ๋Œ€์‹  ์—…๋ฐ์ดํ„ฐ ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import { useState } from 'react';

export default function RequestTracker() {
  const [pending, setPending] = useState(0);
  const [completed, setCompleted] = useState(0);

  async function handleClick() {
    setPending(p => p + 1);
    await delay(3000);
    setPending(p => p - 1);
    setCompleted(c => c + 1);
  }

  return (
    <>
      <h3>
        Pending: {pending}
      </h3>
      <h3>
        Completed: {completed}
      </h3>
      <button onClick={handleClick}>
        Buy
      </button>
    </>
  );
}

function delay(ms) {
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  });
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์นด์šดํ„ฐ๋ฅผ ๋Š˜๋ฆฌ๊ฑฐ๋‚˜ ์ค„์ผ ๋•Œ ํด๋ฆญ ๋‹น์‹œ์˜ state๊ฐ€ ์•„๋‹ˆ๋ผ ์ตœ์‹  state์™€ ๊ด€๋ จํ•˜์—ฌ ์นด์šดํ„ฐ๋ฅผ ๋Š˜๋ฆฌ๊ฑฐ๋‚˜ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

state ํ๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•ด ๋ณด์„ธ์š”. {/implement-the-state-queue-yourself/}

์ด๋ฒˆ ๋„์ „๊ณผ์ œ์—์„œ๋Š” React์˜ ์ž‘์€ ๋ถ€๋ถ„์„ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์‹œ ๊ตฌํ˜„ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค! ์ƒ๊ฐ๋ณด๋‹ค ์–ด๋ ต์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ƒŒ๋“œ๋ฐ•์Šค ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ์Šคํฌ๋กค ํ•˜์„ธ์š”. 4๊ฐœ์˜ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๊ฐ€ ํ‘œ์‹œ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•˜์„ธ์š”. ์ด ํŽ˜์ด์ง€์˜ ์•ž๋ถ€๋ถ„์—์„œ ๋ณด์•˜๋˜ ์˜ˆ์‹œ์™€ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ๋ถ„์˜ ์ž„๋ฌด๋Š” ๊ฐ ์ผ€์ด์Šค์— ๋Œ€ํ•ด ์˜ฌ๋ฐ”๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก getFinalState ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๊ตฌํ˜„ํ•˜๋ฉด ๋„ค ๊ฐ€์ง€ ํ…Œ์ŠคํŠธ๋ฅผ ๋ชจ๋‘ ํ†ต๊ณผํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋‘ ๊ฐœ์˜ ์ธ์ˆ˜๋ฅผ ๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. baseState๋Š” ์ดˆ๊ธฐ state(์˜ˆ: 0)์ด๊ณ , queue๋Š” ์ˆซ์ž(์˜ˆ: 5)์™€ ์—…๋ฐ์ดํ„ฐ ํ•จ์ˆ˜(์˜ˆ: n => n + 1)๊ฐ€ ์ถ”๊ฐ€๋œ, ์ˆœ์„œ๋Œ€๋กœ ์„ž์—ฌ ์žˆ๋Š” ๋ฐฐ์—ด์ž…๋‹ˆ๋‹ค.

์—ฌ๋Ÿฌ๋ถ„์˜ ์ž„๋ฌด๋Š” ์ด ํŽ˜์ด์ง€์˜ ํ‘œ์— ํ‘œ์‹œ๋œ ๊ฒƒ์ฒ˜๋Ÿผ ์ตœ์ข… state๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค!

๋ง‰๋ง‰ํ•œ ๋А๋‚Œ์ด ๋“ ๋‹ค๋ฉด ๋‹ค์Œ ์ฝ”๋“œ ๊ตฌ์กฐ๋กœ ์‹œ์ž‘ํ•ด ๋ณด์„ธ์š”.

export function getFinalState(baseState, queue) {
  let finalState = baseState;

  for (let update of queue) {
    if (typeof update === 'function') {
      // TODO: apply the updater function
    } else {
      // TODO: replace the state
    }
  }

  return finalState;
}

๋นˆ์นธ์„ ์ฑ„์›Œ์ฃผ์„ธ์š”!

export function getFinalState(baseState, queue) {
  let finalState = baseState;

  // TODO: do something with the queue...

  return finalState;
}
import { getFinalState } from './processQueue.js';

function increment(n) {
  return n + 1;
}
increment.toString = () => 'n => n+1';

export default function App() {
  return (
    <>
      <TestCase
        baseState={0}
        queue={[1, 1, 1]}
        expected={1}
      />
      <hr />
      <TestCase
        baseState={0}
        queue={[
          increment,
          increment,
          increment
        ]}
        expected={3}
      />
      <hr />
      <TestCase
        baseState={0}
        queue={[
          5,
          increment,
        ]}
        expected={6}
      />
      <hr />
      <TestCase
        baseState={0}
        queue={[
          5,
          increment,
          42,
        ]}
        expected={42}
      />
    </>
  );
}

function TestCase({
  baseState,
  queue,
  expected
}) {
  const actual = getFinalState(baseState, queue);
  return (
    <>
      <p>Base state: <b>{baseState}</b></p>
      <p>Queue: <b>[{queue.join(', ')}]</b></p>
      <p>Expected result: <b>{expected}</b></p>
      <p style={{
        color: actual === expected ?
          'green' :
          'red'
      }}>
        Your result: <b>{actual}</b>
        {' '}
        ({actual === expected ?
          'correct' :
          'wrong'
        })
      </p>
    </>
  );
}

์ด ํŽ˜์ด์ง€์— ์„ค๋ช…๋œ ๋ฐ”๋กœ ๊ทธ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด React๊ฐ€ ์ตœ์ข… state๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์ž…๋‹ˆ๋‹ค.

export function getFinalState(baseState, queue) {
  let finalState = baseState;

  for (let update of queue) {
    if (typeof update === 'function') {
      // Apply the updater function.
      finalState = update(finalState);
    } else {
      // Replace the next state.
      finalState = update;
    }
  }

  return finalState;
}
import { getFinalState } from './processQueue.js';

function increment(n) {
  return n + 1;
}
increment.toString = () => 'n => n+1';

export default function App() {
  return (
    <>
      <TestCase
        baseState={0}
        queue={[1, 1, 1]}
        expected={1}
      />
      <hr />
      <TestCase
        baseState={0}
        queue={[
          increment,
          increment,
          increment
        ]}
        expected={3}
      />
      <hr />
      <TestCase
        baseState={0}
        queue={[
          5,
          increment,
        ]}
        expected={6}
      />
      <hr />
      <TestCase
        baseState={0}
        queue={[
          5,
          increment,
          42,
        ]}
        expected={42}
      />
    </>
  );
}

function TestCase({
  baseState,
  queue,
  expected
}) {
  const actual = getFinalState(baseState, queue);
  return (
    <>
      <p>Base state: <b>{baseState}</b></p>
      <p>Queue: <b>[{queue.join(', ')}]</b></p>
      <p>Expected result: <b>{expected}</b></p>
      <p style={{
        color: actual === expected ?
          'green' :
          'red'
      }}>
        Your result: <b>{actual}</b>
        {' '}
        ({actual === expected ?
          'correct' :
          'wrong'
        })
      </p>
    </>
  );
}

์ด์ œ React์˜ ์ด ๋ถ€๋ถ„์ด ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!