반응형
대충 만든 비동기 훅을 호출하고 렌더링 전 뒤로가기를 누르면 콘솔에 해당 로그가 나온다.
가비지 컬렉팅 전, 컴포넌트가 버츄얼 돔 상에 없어도 상태 업데이트를 시도할 수 있다.
해당 문제를 막으면서 useCallback을 공부해보자
1. useCallback은 언제 쓰는가.
React.memo 사용 렌더링 최적화시 props로 동일 객체 넘겨주기
의존성 배열에 상태 변화 없음을 알려주기
2. Let's Coding
1. unmount된 컴포넌트의 상태 업데이트를 막는 훅을 보자
주석은 useLayoutEffect를 사용하란 의미다.
(LayoutEffect가 더 먼저 실행된다.)
function useSafeDispatch(dispatch) {
const mountedRef = React.useRef(false)
// to make this even more generic you should use the useLayoutEffect hook to
// make sure that you are correctly setting the mountedRef.current immediately
// after React updates the DOM. Even though this effect does not interact
// with the dom another side effect inside a useLayoutEffect which does
// interact with the dom may depend on the value being set
React.useEffect(() => {
mountedRef.current = true
return () => {
mountedRef.current = false
}
}, [])
return React.useCallback(
(...args) => (mountedRef.current ? dispatch(...args) : void 0),
[dispatch],
)
}
return 콜백은 dispatch 함수의 변경을 허용하고 있다. (의존성 배열)
2. useAsync 비동기 함수를 만든다.
unsafeDispatch는 리액트에 의해 immutable이 보장된다.
function useAsync(initialState) {
const [state, unsafeDispatch] = React.useReducer(asyncReducer, {
status: 'idle',
data: null,
error: null,
...initialState,
})
const dispatch = useSafeDispatch(unsafeDispatch)
const {data, error, status} = state
const run = React.useCallback(
promise => {
dispatch({type: 'pending'})
promise.then(
data => {
dispatch({type: 'resolved', data})
},
error => {
dispatch({type: 'rejected', error})
},
)
},
[dispatch],
)
return {
error,
status,
data,
run,
}
}
의존성 배열에 eslint-disabled를 쓰는건 좋지 않다.
차라리 클로저를 잘 활용하여 목록을 적게 유지하는게 낫다.
3. 다음과 같이 사용한다.
run도 불변이 보장되지만, 의존성 배열에 넣어준 것을 보자.
의존성은 전부 명시적으로 적어주는것이 좋다.
run(dispatch)함수는 reducer의 역할을 하고 있기 때문에, 다형성으로 의존성을 줄인다.
즉 state, reducer 두개만 넣어줘도 대부분 충분한 것이다.
function PokemonInfo({pokemonName}) {
const {data: pokemon, status, error, run} = useAsync({
status: pokemonName ? 'pending' : 'idle',
})
React.useEffect(() => {
if (!pokemonName) {
return
}
run(fetchPokemon(pokemonName))
}, [pokemonName, run])
if (status === 'idle') {
return 'Submit a pokemon'
} else if (status === 'pending') {
return <PokemonInfoFallback name={pokemonName} />
} else if (status === 'rejected') {
throw error
} else if (status === 'resolved') {
return <PokemonDataView pokemon={pokemon} />
}
throw new Error('This should be impossible')
}
심화학습은 아래에서
When to useMemo and useCallback (kentcdodds.com)
반응형
'FrontEnd' 카테고리의 다른 글
[Epic React] Errorboundary로 선언적 에러 핸들링 (3) | 2021.12.04 |
---|---|
[Epic React] async 훅 사용 시 하나의 state를 사용하는 이유 (0) | 2021.12.04 |
[Epic React] useDebug으로 custom hook debugging (0) | 2021.12.04 |
[Epic React] useLayoutEffect, useImperativeHandle, forwardRef (0) | 2021.12.04 |
useEffect는 라이프사이클 훅이 아니다. (0) | 2021.12.04 |