안녕하세요!
훅에 대한 세번째 포스팅입니다.
Hook에대한 공부를하면서,클래스형 컴포넌트를 좋은 방향으로 대체할 수 있겠구나라는 생각을 했습니다.
상탯값관리, Side Effect수행, 공통기능관리 등에대해 알아봤었는데요
오늘은 클래스형 컴포넌트의 생명주기들을 Hook으로 대체하는 법에대해 알아보겠습니다.
아래의 내용들은 이미 공부했었습니다.
링크를 참고해주세요
constructor를 훅으로 작성하는 방법입니다.
constructor는 주로 속성값으로부터 초기 속성값을 계산하는 용도로 사용됩니다.
그리고 componentDidMount보다 빠르게 작업을 처리하는 용도로도 사용합니다.
function Profile ({firstName, lastName}) {
const [name,setName] = useState(`${firstName} ${lastName}`)
const isFirstRef = useRef(true)
if(isFirstRef.current) {
isFirstRef.currnet = false;
// ...
}
// ...
}
속성값으로부터 초기속성값을 계산합니다.
이 코드를 분리해서 아래코드처럼 커스텀훅으로 작성할 수도 있습니다.
import React, { useState, useRef } from 'react'
function useOnFirstRender (func) {
const isFirstRef = useRef(true)
if(isFirstRef.current) {
isFirstRef.current = false;
func()
}
}
useOnFirstRender라는 훅이 완성됐습니다.
useEffect훅은 최초렌더링과 이후 업데이트시 렌더링에도 호출됩니다.
최초렌더링시 호출되지 않도록 작성하기 위해 useRef훅을 이용합니다.
그리고, componentDidUpdate의 매개변수로 이전상탯값과 이전속성값을 전달하기 때문에 이전 값들을 이용하기위해 useRef를 사용합니다.
import React, { useRef, useEffect } from 'react'
function usePrevious (value) {
const valueRef = useRef();
useEffect(()=>{
valueRef.current = value;
},[value])
return valueRef.current;
}
커스텀 훅 usePrevious는 값을 받아서 useRef()훅으로 생성한 valueRef의 current속성에 저장합니다.
그런 뒤 valueRef.current를 반환합니다.
인스턴스에 값을 저장할 수 없는 함수 컴포넌트 특성상 useRef를 저장소로 활용하는 방법을 이용합니다.
의존성목록에 value를 넣어서 , value가 변경됐을 경우에만 valueRef.current속성에 값을 업데이트 하기 때문에 valueRef.current는 항상 이전값을 가지고있습니다.
저장소로 활용할 훅을 만들어으니 componentDidUpdate훅을 만들겠습니다.
export default function Profile (props) {
const [name,setName] = useState(props.name);
const prevUserId = usePrevious(props.userId);
const isMountedRef = useRef(false);
useEffect(()=>{
if(isMountedRef.current) {
if(prevUserId !== props.userId) {
setName(props.name);
}
}else{
isMountedRef.current = true;
}
});
return (
<div>
</div>
)
}
첫 렌더링시에는 else로 분기해서 isMountedRef.current속성을 true로 변경하고, 이 후 렌더링부터는 prevUserId와 props.userId가 다를경우 setName을 호출하는 작업을 수행합니다.
마찬가지로 이런 로직이 자주사용된다면, 로직을 분리해 커스텀훅을 만들 수 있습니다.
function useOnUpdate (func) {
const isMountedRef = useRef(false)
useEffect(()=>{
if(isMountedRef.current) {
func();
}else {
isMountedRef.current = true
}
})
}
export default function Profile (props) {
const [name,setName] = useState(props.name);
const prevUserId = usePrevious(props.userId);
useOnUpdate(()=>{
if(prevUserId !== props.userId) {
setName(props.name);
}
});
return (
<div>
<p>{`${name} , ${prevUserId}, ${props.userId}`}</p>
</div>
)
}
자주사용하는 커스텀 훅들을 모아서 필요시마다 모듈로 호출해 사용하면 좋을 것 같습니다.
getDerivedStateFromProps정적 메서드는 속성값 변경에 따라 상탯값도 변경할때 사용됩니다.
import React, { useState } from 'react'
export default function SpeedIndicator ({speed}) {
const [isFaster, setIsFaster] = useState(false);
const [prevSpeed, setPrevSpeed] = useState(0);
if (speed !== prevSpeed) {
setIsFaster(speed > prevSpeed);
setPrevSpeed(speed)
}
return <p> It's getting faster : {isFaster? 'yes':'no'}</p>
}
forceUpdate의 사용은 지양해야 하지만 필요한 경우 훅으로 구현할 수 있습니다.
function MyComponent() {
const [_,forceUpdate] = useReducer(s=>s+1, 0);
function onClick() {
forceUpdate()
}
}
useReducer함수의 첫 번째 매개변수 reducer가 입력값에 상관없이 입력값+1을 반환하며, 초기값은 0입니다. 따라서 인자 없이 forceUpdate훅을 호출하면 상태값이 +1씩 증가하여, 결과적으로 상태값이 변경되므로 항상 업데이트되게 됩니다.
여기까지 포스팅을 마치겠습니다.