빙응의 공부 블로그
[React]리액트 -State and Lifecycle, hooks 본문
📝State란 무엇일까?
리액트의 핵심인 State는 한글로 상태를 뜻한다.
리액트에서의 상태는 리액트 Component의 상태를 의미한다.
쉽게 말하면 리액트의 변경 가능한 데이터를 State라고 한다.
🚩 중요점
- State는 렌더링이나 데이터 흐름에 사용되는 값만 포함 시켜야 한다.
- 스테이트를 변경할 경우 재렌더링 되기 때문에 성능 저하가 발생할 수 있기 때문이다.
- State는 JavaScript의 객체이다.
위 사진은 클래스 컴포넌트를 나타내며 constructor는 생성자이다.
- 붉은 네모칸이 바로 State를 정의하는 부분이다.
🚩 State는 직접 수정하면 안된다.
이 말의 의미가 무엇이냐면 자바로 관점에서 class의 객체를 직접 수정하면 안되듯이 리액트에서 getter, setter를 사용해야 한다.
// 직접 수정, 잘못된 사용방법
this.state = {
name : 'jsad'
};
// 함수를 통한 수정, 정상적인 사용방법
this.setState({
name: 'jsda'
});
📝Lifecycle
클래스 컴포넌트가 생성되는 시점과 사라지는 시점이 정해져있다.
단! 최근에는 클래스 컴포넌트를 잘 사용하지 않으므로 이것이 있다 정도만 기억해두자
한마디로 정의하자면
Component가 계속 존재하는 것이 아니라, 시간의 흐름에 따라 생성되고 업데이트 되다가 사라진다.
📝 hooks
앞 포스팅에서 리액트 컴포넌트에 대해 배울때 2가지 컴포넌트가 있다고 하였다.
- Function Conponent
- State 사용 불가
- Lifecycle에 따른 기능 구현 불가
- Class Component
- 생성자에서 state를 정의
- setState() 함수를 통해 state 업데이트
- Lifecycle 모듈 제공
이렇게 함수 컴포넌트에 클래스 컴포넌트와 동일한 기능을 사용하게 해주는 것이 바로 hook이다.
function MyComponent(props) {
//state 관련 hook 함수
//Lifecycle 관련 hook 함수
//최적화 관련 hook 함수
return (
<div>
안녕하세요.
</div>
)
}
🚩 useState
말 그대로 State를 사용하기 위한 훅이다.
import React, { useState } from "react";
function Counter(props) {
var count = 0;
return (
<div>
<p>총 {count}번 클릭했습니다.</p>
<button onClick={() => count++}>
클릭
</button>
</div>
);
}
위 함수 컴포넌트는 버튼을 클릭할 때마다 숫자가 올라가는 로직이지만
- count 변수는 State가 아니기에 재렌더링이 일어나지 않는다.
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>
);
}
- 위 처럼 useState를 사용하면 재렌더링을 구현할 수 있다.
- 또한 변수마다 set 함수가 따로 존재한다.!
🚩 useEffect
useEffect는 Side Effect를 수행하기 위한 Hook이다.
Side effect = 효과, 영향
서버에서 데이터를 받아오거나 수동으로 DOM을 변경하는 작업
다른 컴포넌트에 영향을 미칠 수 있어 렌더링이 끝난 이후에 시작해야하는 작업이다.
- useEffect는 리액트 함수 컴포넌트에서 Side Effect를 실행할 수 있게 도와준다.
- 이것은 클래스 컴포넌트의 생명주기 함수를 하나의 기능으로 통합하여 재공한다.
import React, { useState } from "react";
function Counter(props) {
const [count, setCount] = useState(0);
// conponentDidMount, componentDidUpdate와 비슷하게 작동한다.
// 처음 시작, 업데이트 될 때마다 불러오는 함수
useEffect(() => {
document.title = 'You clicked ${count} times';
//컴포넌트가 unmount 될때 호출된다
return () => {
document.title = '컴포넌트 종료' ;
}
});
return (
<div>
<p>총 {count}번 클릭했습니다.</p>
<button onClick={() => setCount(count + 1)}>
클릭
</button>
</div>
);
}
위의 코드는 기존 코드에 useEffect를 추가한 것이다.
// conponentDidMount, componentDidUpdate와 비슷하게 작동한다.
// 처음 시작, 업데이트 될 때마다 불러오는 함수
useEffect(() => {
document.title = 'You clicked ${count} times';
//컴포넌트가 unmount 될때 호출된다
return () => {
document.title = '컴포넌트 종료' ;
}
});
해당 명령어는 클래스 컴포넌트의 conponentDidMount, componentDidUpdate와 비슷하게 동작하며
useEffect 안에 return은 함수 생명주기가 끝나기 전에 호출된다.!
📝 성능 최적화 훅
🚩 useMemo
useMemo는 Memoized value를 리턴하는 훅이다.
Memoization : 최적화를 하기 위해서 사용하는 개념
비용이 많은 함수의 결과를 저장하였다가 같은 입력 값이 들어오면 전에 저장한 것을 리턴하는 개념이다.
const memoizedValue = useMemo(
() => {
return computeExpensiveValue(의존성 변수1, 의존성 변수2);
};
[의존성 변수, 의존성 변수2]
);
import React, { useMemo } from 'react';
function ExpensiveComponent({ a, b }) {
// a와 b 값이 변경될 때만 add 함수가 다시 실행되고 결과를 캐싱
const sum = useMemo(() => {
console.log('Calculating sum...');
return a + b;
}, [a, b]);
return <p>Sum: {sum}</p>;
}
useMemo는 파라미터로 memoizedValue를 생성하는 create 함수와 의존성 배열을 받는다.
- Memoization의 개념처럼 의존성 배열에 들어있는 변수가 변했을 경우에만 새로 create함수를 호출하여 결과값을 반환하며 그렇지 않을 경우 그대로 반환한다.
- useMemo에 들어있는 함수는 렌더링 중에 실행되는 함수이므로 렌더링 중 실행할 수 없는 함수는 넣으면 안된다.
🚩 useCallback
useMemo와 유사하지만 값이 아닌 함수를 반환한다.
const memoizedCallback = useCallback(
() => {
computeExpensiveValue(의존성 변수1, 의존성 변수2);
},
[의존성 변수, 의존성 변수2]
);
컴포넌트가 렌더링할 때마다 매번 함수를 새로 정의하는 것이 아니라 의존성 배열의 값이 바뀐 경우에만 함수를 새로 정의해서 리턴한다.
import React, { useState, useCallback } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
// handleClick 함수를 useCallback으로 감싸서 재사용 가능한 콜백 함수로 만듦
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]); // count가 변경될 때만 함수가 새로 생성됨
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
최적화 정리
- 콜백(callback): 특정 조건이나 상황에 따라 실행되는 함수를 다른 함수의 인자로 전달하는 것. 외부 조건에 의해 실행 여부가 결정
- 메모ization(memoization): 이전에 계산한 결과를 저장해두고, 동일한 입력이 들어오면 저장된 결과를 반환하여 함수의 재실행을 피하는 최적화 기법이다.. 함수의 입력 값에 따라 결과를 캐싱하고 재활용
🚩 useRef
useRef는 Reference를 사용하기 위한 훅이다.
Reference란 특정 컴포넌트에 접근할 수 있는 객체를 의미한다.
useRef은 이 레퍼런스 객체를 반환한다.
Current
- 레퍼런스 객체에는 current라는 속성이 있는데 이것은 현재 참조하고 있는 엘리먼트를 의미한다.
사용법
const refContatiner = useRef(초깃값);
- 파라미터로 초기값을 넣으면 해당 초기값으로 초기화된 레퍼런스 객체를 반환한다.
- 이렇게 반환된 레퍼런스 객체는 컴포넌트의 라이프타임 전체에 걸쳐서 유지된다.
- 즉 언마운트 전까지 유지
import { useRef } from 'react';
function MyComponent() {
const inputRef1 = useRef(null);
const inputRef2 = useRef(null);
const focusInput1 = () => {
inputRef1.current.focus();
};
const focusInput2 = () => {
inputRef2.current.focus();
};
return (
<div>
<input ref={inputRef1} type="text" />
<button onClick={focusInput1}>Focus Input 1</button>
<input ref={inputRef2} type="text" />
<button onClick={focusInput2}>Focus Input 2</button>
</div>
);
}
쉽게 말하면
컴포넌트로 여러 개의 엘리먼트 객체를 만들어도 useRef를 통해 해당 객체의 DOM을 직접 접근이 가능해진다.
📝 Hook의 규칙과 커스텀 Hook
🚩 규칙
1
Hook은 무조건 최상위 레벨에서만 호출되어야 한다.
- 즉, 반복문, 조건문 또는 중첩된 함수들 안에서 Hook을 호출하면 안된다.
- 그렇기에 Hook은 컴포넌트가 렌더링될 때마다 매번 같은 순서로 호출되어야 한다
2
리액트 함수 컴포넌트에서만 Hook을 호출해야 한다.
🚩 커스텀 훅
예제를 통해 커스텀 훅을 만들어보자
import React, { useState, useEffect } from "react";
function UserListItem(props) {
// 상태 변수(isOnline)와 해당 상태를 업데이트할 함수(setIsOnline)를 선언합니다.
const [isOnline, setIsOnline] = useState(null);
// 컴포넌트가 마운트될 때와 언마운트될 때 실행되는 부수 효과 로직을 정의합니다.
useEffect(() => {
// 사용자 상태가 변경될 때 호출될 콜백 함수를 정의합니다.
function handleStatusChange(status) {
setIsOnline(status.isOnline); // 사용자 온라인 상태를 업데이트합니다.
}
// ServerAPI를 통해 해당 사용자의 상태를 구독합니다.
ServerAPI.subscribeUserStatus(props.user.id, handleStatusChange);
// 컴포넌트가 언마운트될 때, 구독을 해제하기 위해 정리(clean-up) 함수를 반환합니다.
return () => {
ServerAPI.unsubscribeUserStatus(props.user.id, handleStatusChange);
};
}, []); // 의존성 배열이 비어있으므로, 컴포넌트가 처음 마운트될 때 한 번만 실행됩니다.
// 사용자 정보를 나타내는 리스트 아이템을 렌더링합니다.
return (
<li style={{ color: isOnline ? 'green' : 'black' }}>
{props.user.name}
</li>
);
}
export default UserListItem;
이 예제 컴포넌트는 사용자의 상태가 온라인인지 아닌지를 검사하는 컴포넌트이다.
이 컴포넌트에서 재사용을 위해 커스텀 훅으로 만들어보자
import React, { useState, useEffect } from "react";
// 커스텀 훅 useUserStatus 정의
function useUserStatus(userId) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
// 사용자 상태가 변경될 때 호출될 콜백 함수
function handleStatusChange(status) {
setIsOnline(status.isOnline); // 사용자 온라인 상태 업데이트
}
// 사용자 상태를 구독
ServerAPI.subscribeUserStatus(userId, handleStatusChange);
// 컴포넌트가 언마운트될 때 구독 해제
return () => {
ServerAPI.unsubscribeUserStatus(userId, handleStatusChange);
};
}, [userId]); // userId가 변경될 때마다 useEffect 다시 실행
return isOnline; // 상태 값 반환
}
// UserListItem 컴포넌트에서 useUserStatus 훅을 사용
function UserListItem(props) {
const isOnline = useUserStatus(props.user.id);
return (
<li style={{ color: isOnline ? 'green' : 'black' }}>
{props.user.name}
</li>
);
}
컴포넌트로부터 중복된 로직을 제거한다.
- 여기서 주의점은 커스텀 훅은 무조건적으로 use로 시작해야한다.
'React' 카테고리의 다른 글
[React]리액트 - CSS (0) | 2024.05.13 |
---|---|
[React]리액트 - Form, Lifting State Up, Inheritance, Context (0) | 2024.05.12 |
[React]리액트 - Event, Condition, List and Keys (0) | 2024.05.10 |
[React]리액트 - Elements, Components, Props (0) | 2024.05.07 |
[React]리액트란 무엇인가? (0) | 2024.05.06 |