Lazy Initialization
은 useState를 사용하여 state를 초기화하는 과정을 lazy(게으르게)하게 실행하는 것을 의미.
import React, { useEffect, useState } from "react";
function getName() {
console.log("사실은 겁나 오래기다리는중...");
return "은수";
}
function App() {
const [name, setName] = useState(getName());
const [num, setNum] = useState(0);
return (
<>
<div>
안녕하세요 {name}! 현재 숫자는{num}입니다
</div>
<button onClick={() => setNum((prevNum) => prevNum + 1)}>
버튼을 클릭해봐봐
</button>
</>
);
}
export default App;
컴포넌트가 매번 렌더링될 때마다 getName
함수를 실행되고 있다는 문제점이 있다.
useState(getName())
에서 getName()함수를 호출해서 return받은 값을 useState에 넘겨주면 해당 값으로 초기화가 일어난다. setNum((prevNum) => prevNum + 1)
이 실행되어 리렌더링이 일어난다.useState(getName())
에서 getName()함수를 호출해서 return받은 값을 useState에 넘겨준다.즉 리렌더링 됐을 때 실행된 getName()의 반환값을 사용하지 않는다. (의미없는 함수실행이 일어났음)
import React, { useEffect, useState } from "react";
function getName() {
console.log("완전 무거운 작업");3
return "은수";
}
function App() {
const [name, setName] = useState(getName);
const [num, setNum] = useState(0);
return (
<>
<div>
안녕하세요 {name}! 현재 숫자는{num}입니다
</div>
<button onClick={() => setNum((prevNum) => prevNum + 1)}>
버튼을 클릭해봐봐
</button>
</>
);
}
export default App;
첫 렌더링
1-1) useState(getName)
을 통해 getName함수 자체를 값으로 넘겨준다.
-> 여기에서는 현재 getName함수 실행이 없다.
1-2) 내부적으로 초기화가 되었는지 여부에 따라 초기화 과정을 진행한다.
-> 현재 첫 렌더링이므로 초기화되지 않은 상태이므로 getName함수가 실행된다.
버튼을 누르면 setNum((prevNum) => prevNum + 1)
이 실행되어 리렌더링이된다.
2-1) 이미 초기화가 첫 렌더링 때 되었기 때문에 초기화 하지 않는다. 즉, 함수를 실행조차 하지 않는다.
지금 코드에서는 getName이 오래 걸리는 작업은 아니지만, getName함수가 오래 걸리는 함수였다면, Lazy Initialize로 불필요한 함수 실행을 막는 것이 굉장이 중요한 영역이 될 것이라는 것을 알게 되었다.
그렇다면 언제 Lazy Initialization을 써야할까? 문서에서는 '비싼 비용의 계산' 이 필요할 때 쓰라고 되어있다. localStorage의 접근, map, filter,find 등의 배열을 조작하는 것들이 그 예가 될 수 있다.
반면 초기 값이 간단한 값이거나 이미 계산된 값인 경우에는 Lazy Initialization을 사용하지 않는게 좋다. 비록 함수가 게으른 초기화로 인해 한번만 호출되지만, 함수를 만드는 비용이 존재하기 때문이다. 아래 같은 예시는 과한 최적화 코드이다.
// 원시값을 리턴
const Counter = () => {
const [count, setCount] = useState(() => 0)
}
// prop 또는 이미 존재하는 변수의 값을 리턴
const Counter = ({ initialCount }) => {
const [count, setCount] = useState(() => initialCount)
}
정리
useState(함수())로 적으면 리렌더링될 때 마다 함수 실행하고, useState(함수)는 초기화할때만 함수 실행된다. 즉, 함수()형태는 함수를 실행시킨다음 초기화여부를 논의하고, 콜백형태는 일단 초기화할지말지 비교하고 필요할 때만 함수를 실행시킨다.
공부하며 느낀점
사실 전에 코딩하면서 useState에 함수를 리턴한적은 없었던 것 같다. 그래서 강의를 들으며 이해를 못했던 것 같기도 하고. (토큰 같은 경우도 State에 담지 않고, 그냥 쓸때마다 토큰을 localStorage에서 불러오는 방식으로 썼었는데 게으른 초기화를 사용하여 State에 담아서 코딩해야겠다는 생각이 들었다.)
출처
[리액트의 useState와 lazy initializatio]()
[Hook 시리즈] Lazy initialization 이 대체 뭔데 그래서