참고사이트: https://ko.reactjs.org/docs/hooks-rules.html#explanation
함수 컴포넌트에서 상태를 관리할 때 React.useState()
훅을 사용한다.
useState()
훅은 상태와 상태 업데이트를 담당하는 함수를 반환한다. 훅에 최초 전달된 값이 컴포넌트의 초기 상태 값이 되며, 함수 컴포넌트가 다시 렌더링 될 때는 항상 이전의 업데이트 된 상태 값이 최신 상태 값이 된다.
const [stateValue, stateUpdater] = useState(initialState);
지연된 초기화
initialState 인자는 함수 컴포넌트 초기 렌더링 시에만 사용된는 state 초깃값을 설정한다. 이후 다시 렌더링 될 때는 이 값이 무시된다. 만약 state 초깃값을 계산하는데 많은 시간이 필요한 경우 초기 렌더링 시에만 실행될 함수를 설정해 지연된 초기화 처리가 가능하다.
const [stateValue, setUpdater] = useState(() => {
// 계산에 적지 않은 시간이 소요될 경우
const initialState = fibonacci(30);
// 계산 이후: 지연된 초기화의 처리 값을 반환
return initialState;
});
localStorage에서 데이터 값을 읽어오는 경우 또한 지연된 초기화 방법을 사용한다.
const initialization = () => {
let countValueKey = 'euid-count';
let value = localStorage.getItem('euid-count');
if (!value) {
localStorage.setItem(countValueKey, JSON.stringify({ count: 1000 }));
}
return value ? JSON.parse(value).count : 100;
};
export function TiltCardContainer(props) {
// 상태 관리 (찌연된 초기화(함수) 활용)
const [count] = useState(initialization);
...
상태관리를 할 시에 여러 상태를 묶어서 관리하는 방법 추천 X
// [상태 관리]
// 여러 상태를 묶어서 관리하는 방법 추천 X
// 관심사의 분리가 되지 않기 때문이다.
const [state] = useState({
count: 10,
name: 'JM',
});
return (
<>
<output>{state.count}</output> <output>{state.name}</output>
묶어서 관리를 하게 된다면, 성능 이슈 유발 (불필요한 리 렌더링 시도)
객체 타입의 상태 관리
useState()훅에서 클래스의 state, setState()와 동일하게 작동되도록 하려면 합성된 객체를 반환해야한다.
const [state, updateState] = useState({
key1: false,
key2: true
});
updateState({
...state,
key2: true
});
합성된 객체를 반환시키지 않으면 바로 그냥 설정한 상태 값으로 바뀌어 버림
이는 클래스의 setState 메서드와 useState의 update하는 함수의 동작 방식이 다르기 때문!
사이드 이펙트(비동기 통신 요청/응답, DOM 조작, 구독/취소 등)는 class 컴포넌트의 render 메서드에서는 다룰 수 없다. useEffect() 훅은 class 컴포넌트의 사이드 이펙트 관리 라이프 사이클 메서드 기능을 모두 처리한다.
useEffect(() => {
// DOM 마운트(렌더링) 이후 콜백(실행)
});
특정 조건에 따라 이펙트 함수를 실행해야 하는 경우, useEffect() 훅의 2번째 인자로 종속성 배열을 설정한다.
useEffect(
() => {
// componentDidMount
},
[]
);
// [상태 관리]
const [a, setA] = useState(1);
useEffect(() => {
console.group('종속성 배열에 a 가 변경된 경우');
console.log('change a: ', a);
console.groupEnd();
}, [a]);
// 관심사 Count
const [count, setCount] = useState(10);
useEffect(() => {
console.group('종속성 배열에 count 가 업데이트 된 경우');
console.log('change count: ', count);
console.groupEnd();
}, [count]);
// 관심사 Name
const [name, setName] = useState('euid');
useEffect(() => {
console.group('종속성 배열에 state.name 이 업데이트 된 경우');
console.log('change name: ', name);
console.groupEnd();
}, [name]);
클래스 컴포넌트와 다르게
useEffect
를 사용하여 상태와 행위를 관심사를 분리하여 나눌 수 있다. 또한 이를 빼내어 커스텀 훅을 만들어 어디서나 재사용할 수 있는 컴포넌트를 만들 수 있다.
이펙트 함수를 통해 설정된 사이드 이펙트는 컴포넌트가 UI에서 제거될 때 정리되어야한다.
이를 수행하기 위해 이펙트 함수에서 클린업 함수를 반환하고 그 함수 내부에서 정리를 실행한다.
useEffect(
() => {
// 구독
return () => {
// 정리(cleanup) 함수
// 구독 취소
}
},
[]
);
이는 메모리 누수 방지를 위해 UI에서 컴포넌트를 제거 하는 직전에 수행된다. 이는 class 컴포넌트의 componentWillUnmount와 유사하게 동작한다고 볼 수 있지만, 실상은 다음 이펙트 함수가 실행될 때마다 클린업 함수가 먼저 실행되어 정리한다.
렌더링 될때마다 클린업 함수가 실행되는 이유
참고 사이트: https://ko.reactjs.org/docs/hooks-effect.html#explanation-why-effects-run-on-each-update
참고 사이트: https://www.npmjs.com/package/prop-types
패키지 설치
yarn add prop-types
참고사이트: https://ko.reactjs.org/docs/typechecking-with-proptypes.html#gatsby-focus-wrapper
SkHeading.propTypes = {
className: PropTypes.string,
as: PropTypes.string.isRequired,
children: PropTypes.string.isRequired,
};
isRequired
가 들어오게 된다면 필수적이라는 의미
mutation 방식이어서 리렌더링 발생 X
useRef의 쓰임새
1. DOM 노드 참조
2. 컴포넌트 렌더링에 영향을 미칮 않으면서 임의의 값을 매 렌더링 시마다 기억하고자 할 때
React의 ref는 주로 DOM 노드 참조 목적으로 사용되지만 컴포넌트 렌더링에 영향을 주지 안흔 값 참조 목적으로 사용된다. React.useRef() 훅은 함수 컴포넌트 내부에서 특정 값을 지속적으로 참조할 때 사용한다.
React.useState() 훅과 달리, useRef() 훅은 현재(current) 값이 변경되어도 컴포넌트가 다시 렌더링되지 않아 애플리케이션 성능을 최적화 할 수 있다.
// useRef() 훅을 사용해 카운트 참조 생성
const countRef = useRef(0);
// 카운트 참조의 현재 값이 변경되어도 컴포넌트는 다시 렌더링 되지 않음
countRef.current = countRef.current + 1;
참고사이트: https://ko.reactjs.org/docs/hooks-reference.html#useref
참고사이트: https://ko.reactjs.org/docs/hooks-reference.html#usecallback