state
React에서의 state는 React 컴포넌트의 상태를 의미한다
=> 리액트 Component의 변경 가능한 데이터
(state는 개발자가 정의해서 사용하며 JavaScript 객체이다)
렌더링이나 데이터 흐름에 사용되는 값만 state에 포함시켜야 함!
=> state가 변경될 경우 component가 재렌더링되기 때문에 렌더링과 데이터 흐름 관련없는 값을 포함하면 불필요한 경우에 component가 다시 렌더링되어 성능을 저하시킬 수 있기 때문
(그렇지 않은 값은 컴포넌트의 인스턴스 필드로 정의하면 됨)
state는 엄밀히 말하면 수정이 가능하긴 하지만 직접 수정 할 수 없다 (하면 안된다)
Hooks
리액트 Component에는 Function Component와 Class Component로 나뉘고 아래와 같은 특징이 있다
함수 컴포넌트는 간결하긴 하지만 클래스 컴포넌트 처럼 state와 Lifecycle에 대한 기능이 따로 없었다
=> 함수 컴포넌트의 이런 기능을 지원하기 위해 나온게 Hooks!
(Hooks를 사용하면 함수 컴포넌트도 클래스 컴포넌트의 기능이 모두 지원)
userState()
state를 사용하기 위한 Hook
(함수 컴포넌트에서는 기본적으로 state를 제공하지 않기 때문에 클래스 컴포넌트처럼 state를 사용하고 싶으면 useState를 사용해야함)
import React, { useState } from "react";
function Counter(props) {
var count = 0;
return(
<div>
<p>총 {count}번 클릭했습니다.</p>
<button onClick={() => count++}>
클릭
</button>
</div>
);
}
위의 코드는 버튼을 클릭할 때 마다 count를 늘려주는 기능을 한다
위와 같이 count를 함수의 변수로 선언하고 사용하면 버튼을 클릭할 때 count의 값은 증가하지만 재렌더링이 일어나지 않아 새로운 count값이 화면에 표시되지 않음
=> state를 사용해서 값이 바뀔 때 마다 재렌더링이 되게끔 해야하는데 함수 컴포넌트에는 해당 기능이 따로 없기 때문에 useState를 사용하여 state를 선언하고 업데이트 해야한다!
useState() 사용법
const [변수명, set함수명] = useState(초기값);
useState를 사용하는 방법에서는 변수 각각에 대해 set함수가 따로 존재한다!
import React, { useState } from "react";
function Counter(props) {
const [count, setCount] = useState(0);
return(
<div>
<p>총 {count}번 클릭했습니다.</p>
<button onClick={() => setCount(count + 1)}>
클릭
</button>
</div>
);
}
위와 같이 state를 사용할 수 있다!
useEffect()
리액트의 함수 컴포넌트에서 Side effect를 수행하기 위한 Hook
(다른 컴포넌트에 영향을 미칠 수 있으며, 렌더링 중에는 작업이 완료될 수 없기 때문에 effect라 하고 이런 작업들이 side에서 실행된다 해서 Side effect 라고 불림)
useEffect는 클래스 컴포넌트에서 제공하는 생명주기함수(componentDidMount, componentDidUpdate, componentWillUnmount)와 동일한 기능을 하나로 통합해서 제공함
useEffect() 사용법
useEffect(이펙트 함수, 의존성 배열);
=> 의존성 배열 안에 있는 변수중에 하나라도 값이 변경되면 이펙트 함수가 실행된다
만약 Effect function이 mount, unmount 시에 단 한 번씩만 실행되게 하고 싶다면
useEffect(이펙트 함수, []);
이렇게 하면 된다!
(이렇게 하면 해당 effect가 props나 state에 있는 어떤 값에도 의존하지 않으므로 여러번 실행 X)
useEffect(이펙트 함수);
=> 만약 의존성 배열을 생략하면 컴포넌트가 업데이트될 때마다 호출 된다!
import React, { useState, useEffect } from "react";
function Counter(props) {
const [count, setCount] = useState(0);
// componentDidMount, compoenentDidUpdate와 비슷하게 작동합니다.
useEffect(() => {
// 브라우저 API를 사용해서 document의 title을 업데이트 합니다.
document.title = `You clicked ${count} times`;
});
return(
<div>
<p>총 {count}번 클릭했습니다.</p>
<button onClick={() => setCount(count + 1)}>
클릭
</button>
</div>
);
}
위의 코드와 같이 의존성 배열 없이 useEffect를 사용하면 리액트는 DOM이 변경된 이후에 해당 이펙트 함수를 실행하라고 받아들인다
=> 기본적으로 처음 컴포넌트가 렌더링될때를 포함해서 매번 렌더링 될 때마다 이펙트가 실행된다고 보면 됨
이펙트는 함수 컴포넌트 안에서 선언되기 때문에 해당 컴포넌트의 props와 state에 접근할 수 있다
=> 위 코드에서는 count라는 state에 접근하여 해당 값이 포함된 문자열을 사용한다
useEffect에서 리턴하는 함수는 컴포넌트가 mount 해제, 즉 unmount될 때 호출됨
=> 결과적으로 useEffect의 리턴 함수의 역할은 componentWillUnmount 함수가 하는 역할과 동일함!
useEffect(() => {
// 컴포넌트가 마운트 된 이후,
// 의존성 배열에 있는 변수들 중 하나라도 값이 변경되었을 때 실행됨
// 의존성 배열에 빈 배열([])을 넣으면 마운트와 언마운트시에 단 한 번씩만 실행됨
// 의존성 배열 생략 시 컴포넌트 업데이트 시마다 실행됨
...
return () => {
// 컴포넌트가 마운트 해제되기 전에 실행됨
...
}
}, [의존성 변수1, 의존성 변수2, ...])
useMemo()
Memoized value를 리턴하는 Hook
=> Memoized value는 최적화를 위해 사용하는 개념으로 비용이 높은, 즉 연산량이 많이 드는 함수의 호출 결과를 저장해 두었다가 같은 입력 값으로 함수를 호출하면 새로 함수를 호출하지 않고 이전에 저장했던 값을 반환한다!
=> 함수 호출 결과를 받기까지 걸리는 시간도 짧아지고 불필요한 중복 연산도 하지 않기 때문에 컴퓨터 자원을 적게 씀
useMemo() 사용법
const memoizedValue = useMemo(
() => {
// 연산량이 높은 작업을 수행하여 결과를 반환
return computeExpensiveValue(의존성 변수1, 의존성 변수2);
},
[의존성 변수1, 의존성 변수2]
);
useMemo Hook은 파라미터로 memoizedValue를 생성하는 create함수와 의존성 배열을 받는다
=> memoization의 개념처럼 의존성 배열에 들어있는 변수가 변했을 경우에만 새로 create 함수를 호출하여 결과값을 반환하고 그렇지 않은 경우에는 기존함수의 결과값을 그대로 반환한다
일반적으로 렌더링이 일어나는 동안 실행되서는 안될 작업을 useMemo의 함수에 넣으면 안됨
(서버에서 데이터를 받아오거나 수동으로 DOM을 변경하는 작업 등)
const memoizedValue = useMemo(
() => computeExpensiveValue(a, b)
);
=> 의존성 배열을 넣지 않을 경우, 매 렌더링마다 함수가 실행됨
const memoizedValue = useMemo(
() => {
return computeExpensiveValue(a, b);
},
[]
);
=> 의존성 배열이 빈 배열일 경우, 컴포넌트 마운트 시에만 호출 됨
useCallback()
useMemo() Hook과 유사하지만 값이 아닌 함수를 반환함
=> 컴포넌트가 렌더링 될 때마다 매번 함수를 새로 정의하는 것이 아니라 의존성 배열의 값이 바뀐 경우에만 함수를 새로 정의해서 리턴해 주는 것이다
useCallback() 사용법
const memoizedCallback = useCallback(
() => {
doSomething(의존성 변수1, 의존성 변수2);
},
[의존성 변수1, 의존성 변수2]
);
동일한 역할을 하는 두 줄의 코드
useCallback(함수, 의존성 배열);
useMemo(() => 함수, 의존성 배열);
만약 useCallback() Hook을 사용하지 않고 컴포넌트 내에 함수를 정의한다면 매번 렌더링이 일어날 때마다 함수가 새로 정의된다!
=> useCallback()을 이용하여 특정 변수의 값이 변한 경우에만 함수를 다시 정의하도록 해서 불필요한 반복 작업을 없애주는 것이다
useRef()
리액트에서 Reference란 특정 컴포넌트에 접근할 수 있는 객체이다
=> useRef()는 이런 레퍼런스를 반환함
레퍼런스 객체에는 refObject.current 처럼 current라는 속성이 있는데 이것은 현재 레퍼런스하고 있는 엘리먼트를 의미한다고 보면 된다!
useRef() 사용법
const refContainer = useRef(초깃값);
=> 파라미터로 초깃값을 넣으면 해당 초깃값으로 초기화된 레퍼런스 객체를 반환한다
만약 초깃값이 null 이라면 current의 값이 null인 레퍼런스 객체가 반환됨
(이렇게 반환된 레퍼런스 객체는 컴포넌트의 라이프타임 전체에 걸쳐서 유지된다)
useRef() Hook은 내부의 데이터가 변경되었을 때 별도로 알리지 않는다
=> 따라서 Ref에 DOM노드가 연결되거나 분리되었을 경우에 어떤 코드를 실행하고 싶다면 Callback ref를 사용해야 함!
(React는 ref가 다른 노드에 연결될 때마다 callback을 호출하게 됨)
Hook의 규칙
1. Hook은 무조건 최상위 레벨에서만 호출해야 한다
=> 따라서 반복문이나 조건문 또는 중첩된 함수들 안에서 Hook을 호출하면 안됨!
Hook은 컴포넌트가 렌더링될 때마다 매번 같은 순서로 호출되어야 한다
2. 리액트 함수 컴포넌트에서만 Hook을 호출해야 한다
=> 일반적인 자바스크립트 함수에서 Hook을 호출하면 안됨
(Hook은 리액트 함수 컴포넌트에서 호출하거나 직접 만든 커스텀 Hook에서만 호출할 수 있다)
Custom Hook 만들기
리액트에서 기본적으로 제공되는 Hook들 이외에 추가적으로 필요한 기능이 있다면 직접 Hook을 만들어서 사용할 수 있다
=> 여러 컴포넌트에서 반복적으로 사용되는 로직을 Hook으로 만들어 재사용하기 위해
이름이 use로 시작하고 내부에서 다른 Hook을 호출하는 하나의 자바스크립트 함수이다
=> 여러 개의 컴포넌트에서 하나의 Custom Hook을 사용할 때 컴포넌트 내부에 있는 모든 state와 effects는 전부 분리되어 있음!!
(리액트에서는 각 Custom Hook 호출에 대해서 분리된 state를 얻게 됨)
'✨ Frontend' 카테고리의 다른 글
React Components and Props (0) | 2024.03.03 |
---|---|
React Rendering Elements (0) | 2024.03.03 |
JSX란? (1) | 2024.03.03 |
리액트의 장점과 단점 (0) | 2024.03.02 |