React State & Hooks (1)

‍박태우·2024년 9월 26일

React Study

목록 보기
3/4

1. State and LifeCycle

1) State 와 Lifecycle 의 정의

  • state : 리액트 Component의 상태 (변경가능한 데이터, 개발자가 정의)
  • 렌더링이나 데이터 흐름에 사용되는 값만 state 에 포함시켜야 한다.
    (State 가 변경되면 컴포넌트가 재렌더링 되기 때문에 성능 저하 가능)
  • 흐름에 해당하지 않는 값은 컴포넌트의 인스턴스 필드로 정의해야 한다.
state == JavaScript 객체

1. 클래스 컴포넌트 에서의 State

  • 아래의 클래스 컴포넌트 (LikeButton)

constructor 가 생성자이다. 내부에 this.state 라는 부분은 현재 컴포넌트의 state를 나타낸다. (클래스 컴포넌트의 경우 위와 같이 생성자에 state 를 정의한다.)

2. State 변경시 주의사항

  • 또한 State 는 직접 수정하면 안된다.

개발자가 의도 한대로 동작 하지 않을 수 있다. (setState 라는 함수를 사용해야한다.)

3. 클래스 컴포넌트에서 State 의 LifeCycle (생명주기)

  1. Mounting : 생성자가 실행, 컴포넌트의 State를 정의한다.
    또한 컴포넌트가 렌더링 되며 이후에 componentDidMount 함수가 호출됨

  2. Updating : 여러번 렌더링 되는 과정, 컴포넌트의 props 가 변경되거나 setState 함수 호출에 의해 state 가 변경되거나, forceUpdate라는 강제 업데이트 함수 호출로 인해 컴포넌트가 다시 렌더링 된다.
    이후 componentDidUpdate 함수가 호출된다.

  3. Unmounting : 컴포넌트가 사라지는 시점 componentWillUnmount 함수가 호출
    => 그러면 Unmount 되는 시점은 ? : 상위 컴포넌트에서 현재 컴포넌트를 더 이상 화면에 표시하지 않게 될때 Unmount 되나고 볼 수 있다. 해당 시점 전에 위 함수가 호출되는 것이다.


    초록 부분 : 생명 주기에 따라 호출 되는 클래스 컴포넌트의 함수
    (라이프 사이클 메소드 : 생명 주기 함수)

위 생명주기 3개 함수 외에도 다른 생명주기 함수가 존재하지만 지금은 클래스 컴포넌트를 거의 사용하지 않는다. (요즘은 함수형 컴포넌트 사용)

2) State 사용하기 (클래스 컴포넌트)

1) Notification.jsx

import React from "react";

const styles = {
    wrapper : {
        margin : 8,
        padding : 8,
        display: "flex",
        flexDirection: "row",
        border : "1px solid grey",
        borderRadius : 18,
    }, 
    messageText : {
        color : "black",
        fontSize : 16,
    },
};

class Notification extends React.Component{
    constructor(props){
        super(props);

        this.state = {}; 
    }

    render(){
        return(
            <div style={styles.wrapper}>
                <span style={styles.messageText}>{this.props.message}</span>
            </div>
        )
    }
}

export default Notification;

위와 같이 Notification.jsx 를 생성함, 여기서는 상태 관리가 필요 없다. 그래서 비워둠

2) NotificationList.jsx

import React from "react";
import Notification from "./Notification";

const reservedNotifications = [
    {
        message: "안녕하세요 123",
    },
    {
        message: "안녕하세요 456",
    },
    {
        message: "안녕하세요 789",
    },
];

var timer;

class NotificationList extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            notifications: [], 
        };
    }

    componentDidMount(){
        const {notifications} = this.state;
        timer = setInterval (()=>{
            if(notifications.length < reservedNotifications.length){
                const index = notifications.length;
                notifications.push(reservedNotifications[index]);
                this.setState({
                    notifications : notifications, // notifications 배열이 업데이트 된다. (1초마다)
                });
            }
            else{
                clearInterval(timer); // 다 수행하면 타이머 종료
            }
        },1000); // 1초마다 정해진 작업 수행 
    }

    render(){
        return(
            <div>
                {this.state.notifications.map((notification)=>{
                    return <Notification message={notification.message}/>
                })}
            </div>
        )
    }
}

export default NotificationList;

클래스 컴포넌트임과 동시에 state 를 가지는 컴포넌트 설정이다.
조건에 맞게 this.state 에 배열을 넣고 해당 배열을 넣어가면서 setState 함수를 이용하여 1초마다 업데이트 하고 있다. componentDidMount 함수를 이용하고 있다.

3) index.js

import NotificationList from './ch06/NotificationList';

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
  <React.StrictMode>
    <NotificationList/>
  </React.StrictMode>
)

(일부 코드 생략) : 위처럼 index.js 에서 실행한다.
이때 React.StrictMode 의 변경으로 인해서 위 경우는 1초에 2개씩 나타난다.

=> 따라서 React.StrictMode를 빼고 아래와같이 쓰면 1개씩 나타나게 된다.

import NotificationList from './ch06/NotificationList';

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
	// 이부분을 삭제함
    <NotificationList/>
  
)

답변 참고 : https://www.inflearn.com/community/questions/619705/notificationlist-jsx-%EC%B1%95%ED%84%B0-6%EC%9E%A5-%EC%8B%A4%EC%8A%B5-%EB%82%B4%EC%9A%A9-%EC%A7%88%EB%AC%B8-%EB%8F%99%EC%8B%9C%EC%97%90-2%EA%B0%9C%EC%94%A9-%EB%82%98%EC%98%B4?focusComment=205023

(최종적으로는 위와같이 지정해준 값이 모두 출력된다. [밑에 3개는 따로 추가함])


2. Hooks (함수 컴포넌트)

1) Hooks의 개념과 useState, useEffect

(1) Hooks

=> 컴포넌트는 위와 같이 클래스 컴포넌트, 함수 컴포넌트 의 두 종류가 있다.

=> Hooks 이 있기 전에는 함수형 컴포넌트에서는 state와 lifecycle 기능을 구현하지 못했지만 Hooks 이 등장하면서 클래스 컴포넌트의 기능을 모두 구현 가능하다.

=> Hooks(갈고리) 라는 말 답게 기능마다 갈고리를 걸어 원하는 시점에 함수가 실행되도록 한 것이다. 그리고 이때 실행되는 함수를 hook 이라고 부르기로 정함. (use.. 로 시작하는 함수들)

(2) useState()

=> 함수 컴포넌트에서 사용하지 않는 state를 사용하기 위한 훅

Counter 라는 함수 컴포넌트의 예시, 위 코드의 경우는 버튼을 클릭 시 변수 값은 변하지만 재렌더링이 일어나지 않아서 새로운 카운트 값이 화면에 표시되지 않는다.

  • useState 사용하기

useState 함수의 리턴값은 위와 같이 [변수명, set함수명] 이 존재한다.

=> 이전의 코드에서 useState를 사용하여 상태가 업데이트 되도록 변경한 코드, 컴포넌트가 재렌더링되는 과정과 동일 (초기 값 : 0)

클래스 컴포넌트에서는 모든 state 값을 setState 값을 이용해서 업데이트
함수 컴포넌트에서는 변수 각각에 대해 set함수가 따로 존재한다.

(3) useEffect()

=> Side effect를 수행하기 위한 훅 (리액트에서는 부작용이 아니다.)
(그냥 effect (영향) 라고 봐도 무방하다.)

side effect : 서버에서 데이터를 받아오거나 수동으로 DOM 을 변경하는 등의 작업을 의미한다.
(이 작업들이 다른 컴포넌트에 영향을 미칠 수 있으며, 렌더링 중에는 작업이 완료될 수 없기 때문) -> 약간 부수적인 작업 느낌

=> 위 기능 (생명주기 함수) 들을 하나로 통합해서 제공하는 것이 useEffect() 이다.

  • useEffect 사용하기

의존성 배열은 이 이펙트가 의존하고 있는 배열로서 내부의 변수 중에 하나라도 값이 변경 되면 이펙트 함수가 실행된다.

=> 이펙트 함수는 처음 컴포넌트가 렌더링 된 이후와 업데이트로 인한 재렌더링 이후에 실행된다. 아래와 같이 mount, unmount 시에 한번만 실행되게 하려면 빈 배열을 넣으면 됨

해당 이펙트가 props 나 state 에 있는 어떤 값에도 의존하지 않는 것이 된다. (여러번 실행되지 않는다.)

생략 시에는 컴포넌트 업데이트 때마다 호출 됨


  • 예시코드 (useEffect 사용 추가)

    클래스에서 사용하던 생명주기 함수의 기능을 동일하게 수행하도록 함
    (브라우저의 탭을 업데이트 하도록 함)
    의존성 배열이 없으므로 새로고침마다 호출
    결과적으로 componentDidMount, componentDidUpdate 와 동일 역할
    또한 해당 함수 컴포넌트의 props state에 접근 가능

  • componentWillUnmount 를 useEffect로 구현해보기

위 강조된 부분은 unmount 될 때 호출됨 즉 return문이 componentWillUnmount의 역할을 한다.

이처럼 두개의 useEffect, useState 훅을 사용할 수 있다.

  • 의존성 배열이 있는 경우의 예시
useEffect(() => {
  fetchUser(userId).then((data) => setUser(data));
  // 마운트 시 실행
 return () =>{
   // 언마운트 시 실행
 }
}, [userId]);

userId 가 변경 될때만 fetchUser 함수가 호출된다.
업데이트 시에 즉 언마운트 가 실행 되고 다시 마운트 가 실행됨
return 문은 언마운트 실행 시 추가 동작 설명

  • 정리

=> 추가적으로 useMemo(), useCallback(), useRef() 그리고 custom Hook 까지 다양한 것이 있지만 그건 다음 아티클에서 알아보자 (중요하고 여려운 내용이라 보충이 필요할 듯)

profile
잘 부탁드립니다.

0개의 댓글