tip-archive icon indicating copy to clipboard operation
tip-archive copied to clipboard

Custom hooks - useFetchDispatch

Open JaeYeopHan opened this issue 4 years ago โ€ข 0 comments

Description

useEffect๋Š” dependency list์— ๊ฑธ๋ ค์žˆ๋Š” ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ ์‹คํ–‰๋œ๋‹ค. ๋˜ํ•œ ์ฒ˜์Œ ๋ Œ๋”๋ง ๋  ๋•Œ๋„ ์‹คํ–‰๋œ๋‹ค. ์ฆ‰ class component์—์„œ์˜ componentDidMount, componentWillUnmount ๊ทธ๋ฆฌ๊ณ  componentDidUpdate lifecycle์—์„œ ์‹คํ–‰์‹œํ‚ฌ ํ•จ์ˆ˜๋ฅผ callback์œผ๋กœ ๋„˜๊ฒจ๋ฐ›๋Š” API์ด๋‹ค.

๋ฐ์ดํ„ฐ๋ฅผ fetchํ•˜๋Š” action์„ ์ปดํฌ๋„ŒํŠธ์˜ useEffect์•ˆ์—์„œ dispatch ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž. (์ฆ‰ saga์—์„œ ์ดˆ๊ธฐ ํ˜ธ์ถœ์„ ํ•˜์ง€ ์•Š๋Š”๋‹ค.) ๊ทธ๋ฆฌ๊ณ  ๊ทธ ๋ฐ์ดํ„ฐ๊ฐ€ pagination์ด ์ ์šฉ๋˜์–ด ์žˆ์–ด์„œ asyncAction.SUCCESS๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ๋งˆ๋‹ค concat๋œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž.

[asyncAction.SUCCESS]: (
  state: IState,
  action: Action<IResponse>,
) => {
  const { dataset } = action.payload

  return {
    ...state,
    ...action.payload,
    dataset: state.dataset.concat(dataset),
  }
},

Problem

route์˜ ๋ณ€๊ฒฝ ๋˜๋Š” /detail ํŽ˜์ด์ง€๋กœ ์ง„์ž…ํ–ˆ๋‹ค๊ฐ€ ๋’ค๋กœ ๋Œ์•„๊ฐ€๊ธฐ๋ฅผ ์ง„ํ–‰ํ•  ๊ฒฝ์šฐ, page number๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์•˜์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  useEffect๊ฐ€ componentDidMount ์‹œ์ ์— ํ˜ธ์ถœ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ค‘๋ณต๋œ ๋ฐ์ดํ„ฐ๊ฐ€ redux์— concat๋œ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ ๋‘ ๊ฐ€์ง€๊ฐ€ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค.

Solution 1. Save as pagination data

dataByPage: {
  [PAGE_NUMBER]: { ... }
}

์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ์ „๋‹ฌ๋˜๋Š” response๋ฅผ ๋ฐ”๋กœ redux์— ์ €์žฅํ•˜์ง€ ์•Š๊ณ  PAGE_NUMBER(ํ˜ธ์ถœํ•œ page number)๋ฅผ key๋กœ ์ €์žฅํ•œ ํ›„,

const dataset = Object.values(dataByPage).reduce((prev, next) => prev.concat(next), [])

์ด๋ ‡๊ฒŒ flatํ•˜๊ฒŒ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•˜๋ฉด ์ค‘๋ณต๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์ €์žฅ๋˜๋Š” ๊ฒƒ์„ ํ”ผํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ฐ์ดํ„ฐ๊ฐ€ fetch๋˜๋Š” ๊ฒƒ์€ ๊ทธ๋Œ€๋กœ์ด๋‹ˆ ๋ถˆํ•„์š”ํ•œ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค๋Š” ๋‹จ์ ์ด ์กด์žฌํ•œ๋‹ค. ๋˜ํ•œ redux์— view์—์„œ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ค‘๊ฐ„ ๋‹จ๊ณ„์˜ ์ƒํƒœ๊ฐ€ ์กด์žฌํ•˜๊ณ  ๋งค๋ฒˆ ๋น„์šฉ์ด ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ข‹์ง€ ์•Š๋‹ค.

(์ด page ๊ธฐ์ค€์œผ๋กœ ์ €์žฅ๋œ ์ƒํƒœ๊ฐ’์„ ๋‹ค๋ฅธ ๊ณณ์—์„œ ์‚ฌ์šฉํ•  ์ผ์ด ์—†์„ ๊ฒƒ์ด๋ผ๊ณ  ํŒ๋‹จ.)

Solution 2. Custom hook

useEffect์™€ useRef๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ custom hook์„ ๋งŒ๋“ ๋‹ค. ๊ทธ๋ฆฌ๊ณ  componentDidMount ์‹œ์ ์—๋Š” ํŠน์ • ์กฐ๊ฑด ํ•˜์—์„œ๋งŒ ํ˜ธ์ถœ๋˜๋„๋ก ๋ถ„๊ธฐ๋ฅผ ํƒœ์›Œ์ค€๋‹ค.

import { useRef, useEffect } from 'react'
import { Procedure } from '../utils/eventUtils'

interface IUseFetchDispatchOption {
  componentDidUpdateCondition?: boolean
  componentDidMountCondition?: boolean
}

export const useFetchDispatch = (
  effectFunction: Procedure,
  deps: any[],
  option: IUseFetchDispatchOption,
) => {
  const didMountRef = useRef(false)
  const { componentDidUpdateCondition = true, componentDidMountCondition = true } = option

  useEffect(() => {
    if (!didMountRef.current) {
      didMountRef.current = true
      if (componentDidMountCondition) {
        effectFunction()
      }
      return
    }

    if (componentDidUpdateCondition) {
      effectFunction()
    }
  }, deps) //eslint-disable-line
}

JaeYeopHan avatar Aug 01 '19 01:08 JaeYeopHan