tip-archive
tip-archive copied to clipboard
Custom hooks - useFetchDispatch
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
}