Frontend ๐Ÿ“š/React

[์ฝ”๋”ฉ์• ํ”Œ] ์‡ผํ•‘๋ชฐ ํ”„๋กœ์ ํŠธ Part10 - ์„ฑ๋Šฅ๊ฐœ์„  2: memo, useMemo, useTransition, useDeferredValue

leejaejae 2023. 10. 29. 16:49

โœ๏ธ memo()๋กœ ์ปดํฌ๋„ŒํŠธ ๋ถˆํ•„์š”ํ•œ ์žฌ๋ Œ๋”๋ง ๋ง‰๊ธฐ
- memo() ์“ฐ๋ ค๋ฉด 'react' ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ๋ถ€ํ„ฐ import ํ•ด์˜ค๋ฉด ๋œ๋‹ค.

import {memo, useState} from 'react'  // memo๋ฅผ import

let Child = memo( function(){  // ์›ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ ์ •์˜ ๋ถ€๋ถ„ ๊ฐ์‹ธ๊ธฐ
  console.log('์žฌ๋ Œ๋”๋ง๋จ')
  return <div>์ž์‹์ž„</div>
})

function Cart(){ 

  let [count, setCount] = useState(0)

  return (
    <Child />
    <button onClick={()=>{ setCount(count+1) }}> + </button>
  )
}

- ์ปดํฌ๋„ŒํŠธ๋ฅผ let ์ปดํฌ๋„ŒํŠธ๋ช… = function( ){ } ์ด๋Ÿฐ ์‹์œผ๋กœ ๋งŒ๋“ค์–ด์•ผ ๊ฐ์Œ€ ์ˆ˜ ์žˆ๋‹ค.
- ๊ทธ๋Ÿผ ์ด์ œ Child๋กœ ์ „์†ก๋˜๋Š” props๊ฐ€ ๋ณ€ํ•˜๊ฑฐ๋‚˜ ๊ทธ๋Ÿฐ ๊ฒฝ์šฐ์—๋งŒ ์žฌ๋ Œ๋”๋ง ๋œ๋‹ค.

Q) ๊ทธ๋Ÿผ memo๋Š” ๋ง‰ ์จ๋„ ๋˜๋Š”๊ฑด๊ฐ€?
- memo๋กœ ๊ฐ์‹ผ ์ปดํฌ๋„ŒํŠธ๋Š” ํ—›๋œ ์žฌ๋ Œ๋”๋ง์„ ์•ˆ์‹œํ‚ค๋ ค๊ณ  ๊ธฐ์กด props์™€ ๋ฐ”๋€ props๋ฅผ ๋น„๊ตํ•˜๋Š” ์—ฐ์‚ฐ์ด ์ถ”๊ฐ€๋กœ ์ง„ํ–‰๋œ๋‹ค.
- props๊ฐ€ ํฌ๊ณ  ๋ณต์žกํ•˜๋ฉด ์ด๊ฑฐ ์ž์ฒด๋กœ ๋ถ€๋‹ด์ด ๋  ์ˆ˜๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ผญ ํ•„์š”ํ•œ ๊ณณ์—๋งŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

โœ๏ธ ๋น„์Šทํ•˜๊ฒŒ ์ƒ๊ธด useMemo
- useEffect์™€ ๋น„์Šทํ•œ ์šฉ๋„, ์ปดํฌ๋„ŒํŠธ ๋กœ๋“œ์‹œ 1ํšŒ๋งŒ ์‹คํ–‰ํ•˜๊ณ  ์‹ถ์€ ์ฝ”๋“œ๊ฐ€ ์žˆ์œผ๋ฉด ๊ฑฐ๊ธฐ ๋‹ด์œผ๋ฉด ๋œ๋‹ค.

import {useMemo, useState} from 'react'

function ํ•จ์ˆ˜(){
  return ๋ฐ˜๋ณต๋ฌธ10์–ต๋ฒˆ๋Œ๋ฆฐ๊ฒฐ๊ณผ
}

function Cart(){ 

  let result = useMemo(()=>{ return ํ•จ์ˆ˜() }, [])

  return (
    <Child />
    <button onClick={()=>{ setCount(count+1) }}> + </button>
  )
}

- ์˜ˆ๋ฅผ ๋“ค์–ด ๋ฐ˜๋ณต๋ฌธ์„ 10์–ต๋ฒˆ ๋Œ๋ ค์•ผํ•˜๋Š” ๊ฒฝ์šฐ
- ๊ทธ ํ•จ์ˆ˜๋ฅผ useMemo ์•ˆ์— ๋„ฃ์–ด๋‘๋ฉด ์ปดํฌ๋„ŒํŠธ ๋กœ๋“œ์‹œ 1ํšŒ๋งŒ ์‹คํ–‰๋œ๋‹ค. 
- dependency๋„ ๋„ฃ์„ ์ˆ˜ ์žˆ์–ด์„œ ํŠน์ • state, props๊ฐ€ ๋ณ€ํ•  ๋•Œ๋งŒ ์‹คํ–‰ํ•  ์ˆ˜๋„ ์žˆ๋‹ค. 

 

โœ๏ธ ์ผ๊ด€๋œ batching & useTransition

โœ๏ธ ๋ฆฌ์•กํŠธ 18๋ฒ„์ „๋ถ€ํ„ฐ ์ถ”๊ฐ€๋œ ๊ธฐ๋Šฅ 1: ์ผ๊ด€๋œ batching
- automatic batching ์ด๋ผ๋Š” ๊ธฐ๋Šฅ์ด ์žˆ๋Š”๋ฐ ์ผ์ข…์˜ ์“ธ๋ฐ์—†๋Š” ์žฌ๋ Œ๋”๋ง ๋ฐฉ์ง€๊ธฐ๋Šฅ์ด๊ณ  batching์ด๋ผ๊ณ  ํ•œ๋‹ค.

fetch().then(() => {
    setCount(1)   //์žฌ๋ Œ๋”๋ง๋จ
    setName(2)   //์žฌ๋ Œ๋”๋ง๋จ
})

- ๊ทผ๋ฐ ๋ฌธ์ œ๋Š” ajax ์š”์ฒญ, setTimeout ์•ˆ์— state ๋ณ€๊ฒฝ ํ•จ์ˆ˜๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ batching์ด ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค.
- ๋ฆฌ์•กํŠธ 17๋ฒ„์ „๊นŒ์ง„ ๊ทธ๋Ÿฐ ์‹์œผ๋กœ ์ผ๊ด€์ ์ด์ง€ ์•Š๊ฒŒ ๋™์ž‘ํ–ˆ๋Š”๋ฐ 18๋ฒ„์ „ ์ดํ›„๋ถ€ํ„ฐ๋Š” ์–ด๋”” ์žˆ๋“  ์žฌ๋ Œ๋”๋ง์€ ๋งˆ์ง€๋ง‰ 1๋ฒˆ๋งŒ ๋œ๋‹ค.
- batching ๋˜๋Š”๊ฒŒ ์‹ซ๊ณ  state ๋ณ€๊ฒฝ ํ•จ์ˆ˜ ์‹คํ–‰๋งˆ๋‹ค ์žฌ๋ Œ๋”๋ง์‹œํ‚ค๊ณ  ์‹ถ์œผ๋ฉด flushSync๋ผ๋Š” ํ•จ์ˆ˜ ์‚ฌ์šฉํ•˜๋ฉด ๋จ.

โœ๏ธ ๋ฆฌ์•กํŠธ 18๋ฒ„์ „๋ถ€ํ„ฐ ์ถ”๊ฐ€๋œ ๊ธฐ๋Šฅ 2 : useTransition ์ถ”๊ฐ€๋จ
- ๋ Œ๋”๋ง์‹œ๊ฐ„์ด ๋งค์šฐ ์˜ค๋ž˜๊ฑธ๋ฆฌ๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ๋‹ค ์น˜๊ณ  
- ๋ฒ„ํŠผ ํด๋ฆญ, ํƒ€์ดํ•‘ํ•  ๋•Œ๋งˆ๋‹ค ๊ทธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•ด์•ผํ•œ๋‹ค๋ฉด ๋ฒ„ํŠผ ํด๋ฆญ, ํƒ€์ดํ•‘ ๋ฐ˜์‘์†๋„๋„ ๋Š๋ ค์ง„๋‹ค.
- ์ด๋Ÿด ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด useTransition ๊ธฐ๋Šฅ์ด๋‹ค.

import {useState, useTransition} from 'react'

let a = new Array(10000).fill(0)

function App(){
  let [name, setName] = useState('')
  let [isPending, startTransition] = useTransition()
  
  return (
    <div>
      <input onChange={ (e)=>{ 
        startTransition(()=>{
          setName(e.target.value) 
        })
      }}/>

      {
        a.map(()=>{
          return <div>{name}</div>
        })
      }
    </div>
  )
}

- useTransition() ์“ฐ๋ฉด ๊ทธ ์ž๋ฆฌ์— [๋ณ€์ˆ˜, ํ•จ์ˆ˜]๊ฐ€ ๋‚จ๋Š”๋‹ค.
- ๊ทธ ์ค‘ ์šฐ์ธก์— ์žˆ๋Š” startTransition() ํ•จ์ˆ˜๋กœ state๋ณ€๊ฒฝํ•จ์ˆ˜ ๊ฐ™์€๊ฑธ ๋ฌถ์œผ๋ฉด ๊ทธ๊ฑธ ๋‹ค๋ฅธ ์ฝ”๋“œ๋“ค๋ณด๋‹ค ๋‚˜์ค‘์— ์ฒ˜๋ฆฌํ•ด์ค€๋‹ค.(ํŠน์ • ์ฝ”๋“œ์˜ ์‹คํ–‰์‹œ์ ์„ ๋’ค๋กœ ์˜ฎ๊ฒจ์ฃผ๋Š” ๊ฒƒ)
- ๊ทธ๋ž˜์„œ <input> ํƒ€์ดํ•‘ ๊ฐ™์ด ์ฆ‰๊ฐ ๋ฐ˜์‘ํ•ด์•ผํ•˜๋Š” ๊ฑธ ์šฐ์„ ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค. 
- ๋ฌผ๋ก  ๊ทผ๋ณธ์ ์ธ ์„ฑ๋Šฅ๊ฐœ์„ ์€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— html์ด ๋งŽ์œผ๋ฉด ์—ฌ๋Ÿฌ ํŽ˜์ด์ง€๋กœ ์ชผ๊ฐœ๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

๐Ÿ’‍โ™€๏ธ isPending์€ ์–ด๋””๋‹ค ์“ฐ๋ƒ๋ฉด 
- startTransition() ์œผ๋กœ ๊ฐ์‹ผ ์ฝ”๋“œ๊ฐ€ ์ฒ˜๋ฆฌ์ค‘์ผ ๋•Œ true๋กœ ๋ณ€ํ•˜๋Š” ๋ณ€์ˆ˜

{
  isPending ? "๋กœ๋”ฉ์ค‘๊ธฐ๋‹ค๋ฆฌ์…ˆ" :
  a.map(()=>{
    return <div>{name}</div>
  })
}

 

 

โœ๏ธ useDeferredValue

- startTransition() ์ด๊ฑฐ๋ž‘ ์šฉ๋„๊ฐ€ ๋˜‘๊ฐ™์€๋ฐ state ์•„๋‹ˆ๋ฉด ๋ณ€์ˆ˜ ํ•˜๋‚˜๋ฅผ ์ง‘์–ด๋„ฃ์„ ์ˆ˜ ์žˆ๊ฒŒ ๋˜์–ด ์žˆ๋‹ค.
- ๊ทธ๋ž˜์„œ ๊ทธ ๋ณ€์ˆ˜์— ๋ณ€๋™์‚ฌํ•ญ์ด ์ƒ๊ธฐ๋ฉด ๊ทธ๊ฑธ ๋Šฆ๊ฒŒ ์ฒ˜๋ฆฌํ•ด์ค€๋‹ค.

import {useState, useTransition, useDeferredValue} from 'react'

let a = new Array(10000).fill(0)

function App(){
  let [name, setName] = useState('')
  let state1 = useDeferredValue(name)
  
  return (
    <div>
      <input onChange={ (e)=>{ 
          setName(e.target.value) 
      }}/>

      {
        a.map(()=>{
          return <div>{state1}</div>
        })
      }
    </div>
  )
}

- useDeferredValue ์•ˆ์— state๋ฅผ ์ง‘์–ด๋„ฃ์œผ๋ฉด ๊ทธ state๊ฐ€ ๋ณ€๋™์‚ฌํ•ญ์ด ์ƒ๊ฒผ์„ ๋•Œ ๋‚˜์ค‘์— ์ฒ˜๋ฆฌํ•ด์ฃผ๊ณ 
- ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ๋Š” let state์— ์ €์žฅํ•ด์ค€๋‹ค.

 


๋ณธ ํฌ์ŠคํŒ…์€ << React ๋ฆฌ์•กํŠธ ๊ธฐ์ดˆ๋ถ€ํ„ฐ ์‡ผํ•‘๋ชฐ ํ”„๋กœ์ ํŠธ๊นŒ์ง€! >> ๊ฐ•์˜๋ฅผ ์ฐธ๊ณ ํ•ฉ๋‹ˆ๋‹ค.