제목이 클릭을 유발하는 자극적인 글일 수 있지만 부분적으로는 사실이다. 아래 두개의 예제를 살펴보자
// 예제 1
const Counter = () => {
const [count, setCount] = useState(
Number.parseInt(window.localStorage.getItem(cacheKey))
)
useEffect(() => {
window.localStorage.setItem(cacheKey, count)
}, [cacheKey, count])
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount((prevCount) => prevCount - 1)}>-</button>
<button onClick={() => setCount((prevCount) => prevCount + 1)}>+</button>
</div>
)
}
// 예제 2
const Counter = () => {
const [count, setCount] = useState(() =>
Number.parseInt(window.localStorage.getItem(cacheKey))
)
useEffect(() => {
window.localStorage.setItem(cacheKey, count)
}, [cacheKey, count])
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount((prevCount) => prevCount - 1)}>-</button>
<button onClick={() => setCount((prevCount) => prevCount + 1)}>+</button>
</div>
)
}
두개의 차이점을 눈치챌수 있나요? 만약 그렇다면 눈썰미가 좋네요 🔬 그렇지 않다면 useState 호출 부분을 힌트로 주겠습니다.
// 예제 1
const [count, setCount] = useState(
Number.parseInt(window.localStorage.getItem(cacheKey))
)
// 예제 2
const [count, setCount] = useState(() =>
Number.parseInt(window.localStorage.getItem(cacheKey))
)
지금은 어떤가요? 😊 둘이 같아 보이나요? 좋아요 바로 해답을 알려드릴게요 😂
두 코드의 차이는 상태의 초기화에 있습니다. 첫번째 예제는 localStorage로 부터 값을 가져오고 이걸 integer로 파싱 합니다. 그리고 count 상태에 초깃값으로 설정을 하게 됩니다.
// 예제 1
const [count, setCount] = useState(
Number.parseInt(window.localStorage.getItem(cacheKey))
)
두번째 예제는 거의 유사합니다. localStorage로 부터 값을 가져오고 integer로 변환하여 값을 변환해주는 함수를 전달하였다는것 외에는 말이죠
// 예제 2
const [count, setCount] = useState(() =>
Number.parseInt(window.localStorage.getItem(cacheKey))
)
화살표 함수의 값을 반환해주는 간결함 덕분에 첫번째 예제와 두번째 예제의 차이는 4글자 밖에 안됩니다. 초깃값으로 어떤 값을 가지려하는지에 따라 단지 4글자를 추가해주는 것은 React 함수 컴포넌트의 성능을 증가시켜줄 수 있습니다.
useState 함수에 값 대신에 함수를 전달해주는 것을 지연 초기화라고 합니다. useState문서에 따르면 초깃값을 가져오는데 비용이 비싼 연산을 수행해야 하는 경우 useState와 지연 초기화를 사용하라고 하고 있습니다. 지연 초기화 함수는 상태가 생성될때 한번만 수행되기 때문입니다. 연속된 리렌더링에서 함수는 무시됩니다.
주의 사항으로써 useState hook은 Counter가 렌더 되었을때 첫번째만 동작합니다. 초깃값과 함께 count 상태를 생성합니다. 그리고 나서 setCount를 호출하면 Counter 함수는 다시 호출되어지고 count는 업데이트된 값을 가지게 됩니다. 그리고 이 리렌더링은 count 상태가 바뀔때마다 발생합니다. 그 동안, 그 초깃값은 다시는 사용되지 않습니다.
첫번째 예제에서는 모든 리렌더에 대해 localStorage의 값을 읽지만 우리는 최초 렌더에만 필요합니다. 우리는 불필요한 연산을 많이 수행하게 되는 것입니다. 지연 초기화를 사용한 두번째 예제는 불필요한 연산을 예방해줍니다.
아직 조금 헷갈리나요? 첫번째 예제를 재작성 해보고 이게 좀 더 명확히 해주길 희망합니다. localStorage로부터 useState로 직접 값을 전달해주는 대신에 변수에 값을 저장하고 useState에 대신 전달해주겠습니다.
// 예제 1
const Counter = () => {
const initialValue = Nubmer.parseInt(window.localStorage.getItem(cacheKey))
const [count, setCount] = useState(initialValue)
// 생략
}
이제 리렌더하고 Counter를 다시 호출할때마다 localStorage에서 value를 매번 가져온다는 것을 알 수 있습니다. 최초 실행시에만 필요할뿐인데도 말이죠.
두번째 예제는 useState에 리렌더링 될때마다 함수를 전달합니다. 그렇지만 useStaet는 최초에만 실행합니다. 이게 지연 초기화를 사용하는 이유입니다.
그리고 만약 지연 초기화 함수 전체를 작성한다면 두 예제의 차이점은 좀 더 명확해질 것입니다.
// 예제 2
const Counter = () => {
const [count, setCount] = useState(function() {
return Number.pasreInt(window.localStorage.getItem(cacheKey))
})
// 생략
}
지연 초기화가 최초에만 실행이 되기 때문에 매번 사용할 수 도 있을 것입니다.
// 원시값 반환
const Counter = () => {
const [count, setCount] = useState(() => 0)
// 생략
}
// prop이나 존재하던 변수로부터 반환
const Counter = ({ initialCount }) => {
const [count, setCount] = useState(() => initialCount)
// 생략
}
이러한 경우에 초기값은 단순한 값이나 이미 계산된 값입니다. 함수가 최초 한번만 호출이 될지라도 함수를 매번 생성하는 비용이 여전히 발생합니다. 단순한 값을 반환하는 것보다 함수를 매번 생성하는 비용이 더 높을수 있습니다. 과최적화가 될 수 있습니다.
그럼 언제 지연 초기화를 사용하여야 할까요? 문서에 따르면 "비용이 비싼 연산"에 사용하라고 합니다. localStorage로 부터 값을 읽는것은 비용이 비싼 연산일 수 있습니다. map, filter, find등을 배열을 사용하는 것도 마찬가지 입니다. 값을 얻기 위해 함수를 호출해야 하는 경우, 지연 초기화를 사용할 수 있을만큼 충분히 비싼 계산일 수 있습니다.
저는 현재 date/time을 상태에 부여해야할때 사용합니다.
const Clock = () => {
const [time, setTime] = useState(() => new Date())
useEffect(() => {
const intervalId = setInteral(() => {
setTime(new Date())
}, 1000)
return () => {
clearInterval(intervalId)
}
}, [tickAmount])
return <p>{time.toLocaleTimeString()}</p>
}
출처: https://www.benmvp.com/blog/four-characters-optimize-react-component/
'Frontend > React' 카테고리의 다른 글
React theStateReducerPattern (번역글) 🤔 (0) | 2022.04.24 |
---|---|
React React-query InfiniteQuery 예제 ∞ (0) | 2022.04.23 |
React conatiner/presentational -> hook 📝 (0) | 2022.04.20 |
React Redux-saga's work flow from component 🙋♂️ (0) | 2022.04.19 |
React Jsx 🌱 (0) | 2022.04.18 |