
memorization은 컴퓨터 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술이다.
이런 memorization이 왜 react에 필요한지에 알기 위해선 re-rendering에 대해 알아야한다.
위의 그림이 컴포넌트 구조를 나타낼 때, 가장 상위 컴포넌트인 1번 컴포넌트가 리렌더링 조건에 맞아 리렌더링이 일어난다고 하자. 그러면 자식 컴포넌트인 2~7번의 컴포넌트 전부가 리렌더링이 일어나게 된다.
딱 보기에도 리엑트의 장점인 부분 리렌더링의 기능이 돋보이지 않게 된다. 리렌더링이 필요하지 않은 자식 컴포넌트까지 모두 바뀌어 버린다면 자원의 낭비가 클 것으로 예상되기 때문에 우리는 memorization으로 필요한 부분만 리렌더링 되게 사용하는 것이다.
React.memo를 통해 부모컴포넌트의 state 변경으로 본인의 props가 변경하지 않는 이상 React.memo를 한 컴포넌트는 리렌더링 되지 않는다.
- 부모 컨포넌트 안에 자식 컴포넌트로 Box1, Box2이 있을 때 사용 방법
export default React.memo(Box1); export default React.memo(Box2);
useCallback은 인자로 들어오는 함수를 memorization하는 것이다.
부모 컨포넌트가 리렌더링 되어도 useCallback으로 감싸준 함수는 리렌더링이 되지 않는 것이다.
const initialize = useCallback(()=>{
setCount(0);
},[]);
이렇게 작성한다면 부모 컨포넌트가 리렌더링 되어도 useCallback안에 있는 함수는 리렌더링이 되지 않는다. useCallback은 의존성 배열을 두번째 인자로 가지고 있는데, 의존성 배열안에 변경시에 함수를 실행하도록 하는 값을 넣으면 된다.
useMemo는 값을 캐싱한다.
어떤 자식 컴포넌트 Heavy가 있고, 이 컴포넌트 안에서
엄~~청 많은 계산이 필요한 함수의 리턴 값이 value에 저장된다고 하자.
const value = veryHeavyWork();
부모 컨포넌트가 리렌더링 될 때, 자식 컴포넌트인 Heavy가 계속 리렌더링 되어야하는데, 그렇게 되면 value값을 계속해서 다시 실행하느랴 엄청난 자원을 들여야 한다.
이 value값이 Heavy컴포넌트에서 독자적으로 사용되는 값이라거나 value와 상관없는 값이 부모 컨포넌트에서 변경되어도 자식 컴포넌트라는 이유로 무거운 계산이 계속 리렌더링 된다면, 우리는 useMemo로 value값을 캐싱해서 불필요한 과정을 생략할 수 있다.
const value = useMemo(()=> veryHeavyWork(), []);
마찬가지로 useMemo도 두번째 인자 값으로 의존성 배열을 가지는데, 의존성 배열안의 값이 변경될 때 실행하게 해달라는 의미를 가진다.
💡 그렇다면 모든 값에 memo 값을 넣으면 되겠네!
안된다. memo는 말 그대로 메모리에 공간을 확보해서 값을 맡겨두는 것인데 모든 값에 memorization을 해서 메모리를 불필요하게 낭비하게 된다면 오히려 성능이 악화가 될 수 있다.
커스텀 훅이란 말 그대로 state를 관리하기 위한 hook을 커스텀하게 만드는 것이다.
- 이름은 use로 시작해야 한다. (ex. useInput)
- 파일 이름은 원하는대로 만들어도 된다. (하지만 명시적으로 만들자!)
만약 우리가 input을 많이 받아야 하는 컴포넌트를 만든다고 하자.
그럴 때 우리는 input의 갯수에 따라 계속해서 useState과 이벤트 핸들러 함수를 만들어야 할 것이다.
custom hook 없는 컴포넌트
const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [nickname, setNickname] = useState(""); const [gender, setGender] = useState(""); const [age, setAge] = useState(""); . . . const onChangeEmailHandler = (e) => {} //생략 const onChangePasswordHandler = (e) => {} //생략 const onChangeNicknameHandler = (e) => {}//생략 const onChangeGenderHandler = (e) => {} //생략 . . .
handler 안의 내용은 event에서 target의 value를 가져오는 것으로 똑같은 코드가 input의 갯수에 따라 반복되는 것을 볼 수 있다.
이때 우리는 custom hook을 사용해서 코드 중복을 줄일 수 있게 된다.
useInput.js 파일을 만들어서 안에 중복되는 코드를 작성한다면
useInput.js
const useInput = () => { //input값 state 관리 const [value, setValue] = useState(''); //input 안의 값을 value에 넣어주는 함수 const handler = (e) => { setValue(e.target.value); } return [value, handler]; }; export default useInput;
useInput 훅을 사용한 컴포넌트
const [email, onEmailHandler] = useInput(); const [password, onPasswordHandler] = useInput(); ...
이렇게 input의 value값과 핸들러를 간편하게 작성할 수 있게된다!