useState와 reducer의 효율성에 대해서 공부하다가,
리렌더링 시마다 값을 호출하는 문제에 대한 글을 봤다.
만약 state값을 초기화할때 number, string같은 값이 아니라 함수를 이용해서 생성하고 싶을 땐 어떻게 할까?
1. useState
import { useState } from 'react';
const initialMaker = () => {
console.log('initialMaker Called');
const init = 0;
return init;
}
const TestComponent = () => {
console.log('testComponent called');
const [state, setState] = useState(initialMaker);
const stateAdd1 = () => {
setState((prev) => prev + 1);
};
return <button onClick={stateAdd1}>현재 state는 {state}입니다. </button>;
};
export default TestComponent;
이렇게 하면 리렌더시에는 호출을 안하고 첫 mount에만 함수를 실행하고 그 return값을 저장한다.
중요한것은
const [state, setState] = useState(initialMaker());
처럼 함수를 호출해서 쓰면 안된다는 것이다.
더 정확하게 말하면 써도 되지만 효율성이 구리다.
무엇을 집어넣든 useState()안에 들어가는 값은 첫 한번만 state에 저장되는게 맞지만
(state자체가 오염되지는 않는다는 뜻임)
함수 호출식으로 넣어버리면 값을 저장하던 안하든 일단 호출은 하게 돼서 계속 호출해버린다.
아주 비효율적이다.
import { useState } from 'react';
const initialMaker = () => {
console.log('initialMaker Called');
const init = Math.random();
return init;
}
const TestComponent = () => {
console.log('testComponent called');
const [state, setState] = useState(initialMaker());
const stateAdd1 = () => {
setState((prev) => prev + 1);
};
return <button onClick={stateAdd1}>현재 state는 {state}입니다. </button>;
};
export default TestComponent;
이렇게 보면 쉬운데, 계속 호출이 일어나지만 state가 random으로 update안되고 호출만 된다.
2. useReducer
reducer도 똑같이 생겨서 똑같을 것 같아서 실험해봤다.
const initialMaker = () => {
console.log('initialMaker Called');
const init = 0;
return init;
};
const reducer = (state, action) => {
console.log('reducer called');
switch (action.type) {
case 'add':
return state + 1;
default:
return 0;
}
};
const TestComponent = () => {
console.log('testComponent called');
const [state, dispatch] = useReducer(reducer, initialMaker);
return <button onClick={() => dispatch({ type: 'add'})}>현재 state는 {state}입니다. </button>;
};
export default TestComponent;
이렇게 하면 mount시에만 호출...! 할것 같지만 에러를 준다.
값으로 주면 되긴한다... 아래와 같이..
const initialMaker = () => {
console.log('initialMaker Called');
const init = 0;
return init;
};
const reducer = (state, action) => {
console.log('reducer called');
switch (action.type) {
case 'add':
return state + 1;
default:
return 0;
}
};
const TestComponent = () => {
console.log('testComponent called');
const [state, dispatch] = useReducer(reducer, initialMaker());
return <button onClick={() => dispatch({ type: 'add' })}>현재 state는 {state}입니다. </button>;
};
export default TestComponent;
이렇게 해서 값으로 건네주면 되지만, 그럼 리렌더링 될때마다 initialMaker도 호출해 버린다.
아까도 말햇듯이 효율성이 매우 안좋다.(리렌더할때마다 불러오기 때문에)
그래서 3번째 인자를 줄 수 있다. 리액트에서 다 만들어놨다.
const initialMaker = () => {
console.log('initialMaker Called');
const init = 0;
return init;
};
const reducer = (state, action) => {
console.log('reducer called');
switch (action.type) {
case 'add':
return state + 1;
default:
return 0;
}
};
const TestComponent = () => {
console.log('testComponent called');
const [state, dispatch] = useReducer(reducer, undefined, initialMaker);
return <button onClick={() => dispatch({ type: 'add' })}>현재 state는 {state}입니다. </button>;
};
export default TestComponent;
이렇게 주면 mount시에만 initialMaker를 호출한다.
게다가 undefined말고 값을 전달해서 함수를 생성할 수 있다.
initialMaker(undefiend)를 실행한 값을 state에 초기값으로 저장하고 그 다음부터는 호출하지 않는다.