Hooks

- 함수 컴포넌트는 클래스 컴포넌트와는 달리 state 사용이랑 Lifecycle 사용이 불가능하다. 그래서 이를 보완하기 위해 Hooks을 이용한다.
- 갈고리라는 뜻, 마치 갈고리를 거는 것처럼 끼워서 사용하는 듯함
- 이름 앞에 use로 시작해야 함(약속)

 

 

 

useState() : state를 사용하기 위한 Hook

- const [변수명, set함수명] = useState(초기값);

 

 

useEffect() : Side effect를 수행하기 위한 Hook

- Side effect = 효과, 영향
- useEffect(이펙트 함수, 의존성 배열);
- 의존성 배열이 []일 경우 mount, unmount 시 단 한 번씩만 실행됨
- 의존성 배열을 생략하면 컴포넌트가 업데이트 될 때마다 호출 됨



 

useMemo() : Memoized value를 리턴하는 Hook

- Memoization : 최적화를 위해, 비용이 높은, 연산량이 많은 함수의 호출 결과를 저장해놓았다가, 나중에 필요할 때 반환
- 의존성 배열을 넣지 않을 경우, 매 렌더링마다 함수가 실행 됨
- 의존성 배열이 빈 배열일 경우, 컴포넌트 마운트 시에만 호출 됨

 

 

useCallback() : useMemo() Hook과 유사하지만 값이 아닌 함수를 반환

- useCallback(함수, 의존성 배열);
- useMemo(() => 함수, 의존성 배열);
- 위 두줄의 코드는 동일한 역할을 한다.

 

 

useRef() : Reference를 사용하기 위한 Hook

- Reference : 특정 컴포넌트에 접근할 수 있는 객체
- refObject.current : current는 현재 참조하고 있는 Element
- const refContainer = useRef(초깃값);
- 변경가능한 current
- 내부의 데이터가 변경되었을 때 별도로 알리지 않는다.
- Callback ref는 내부의 데이터가 변경 시, 알림을 받을 수 있음



 

Hook의 규칙

- 무조건 최상위 레벨에서만 호출해야 한다.
- 컴포넌트가 렌더링될 때마다 매번 같은 순서로 호출되어야 한다.
- 리액트 함수 컴포넌트에서만 Hook을 호출해야 한다.

useEffect()가 if문 안에 있다. 잘못된 사용법이다. Hook은 무조건 최상위 레벨에서만 호출해야 한다.



 

Custom Hook 만들기

- 여러 컴포넌트에서 반복적으로 사용되는 로직을 Hook으로 만들어 재사용하는 방법
- 함수역할을 하지만 엄연히 Hook 이기 때문에 꼭 use로 시작해야 한다!
- 여러개의 컴포넌트에서 하나의 Custom Hook을 사용할 때 컴포넌트 내부에 있는 모든 state와 effects는 전부 분리되어 있다.
- 각 Custom Hook 호출에 대해서 분리된 state를 얻게 됨!
- 각 Custom Hook의 호출 또한 완전히 독립적이다.

온라인인지 오프라인인지 알려주는 컴포넌트이다. 사용자가 온라인인지 오프라인인지 알려주는 부분을 따로 추출하여, Custom Hook을 만들 예정이다.

 

 

다음과 같이 따로 추출해서 useUserStatus()라는 Custom Hook을 만들었다.

 

 

다음과 같이 함수마냥 사용하여 중복되는 부분에 붙여넣기 하면 된다.

비록 함수같은 역할을 하지만 엄연히 Hook이기 때문에 앞에 use로 시작하게끔 이름을 설정해야 한다.

 

 

Hook들 사이에서 데이터를 공유하는 방법

 

'React' 카테고리의 다른 글

[React] Event  (0) 2024.08.08
[React] Hooks 실습  (0) 2024.08.07
[React] State and Lifecycle  (0) 2024.08.03
[React] Components 와 Props  (0) 2024.08.01
[React] Rendering Elements  (0) 2024.07.31

State (react의 핵심중의 핵심)

- 한글로 상태라는 뜻
- 리액트에서는 Component의 상태
- 리택트 Component에서 변경 가능한 데이터
- 렌더링이나 데이터 흐름에 사용되는 값만 state에 포함시켜야 함!
- State는 JavaScript 객체이다
- 직접 수정할 수 없다 (수정하면 안된다.)

- constructor (생성자)

 

 

 

 

Lifecycle

- 생명주기 라는 뜻
- 리액트 Component의 생명주기
- Component가 계속 존재하는 것이 아니라, 시간의 흐름에 따라 생성되고 업데이트 되다가 사라진다.

 

 

 

State 실습

import React from "react";

const styles = {
    wrapper: {
        margin: 8,
        padding: 8,
        display: "flex",
        flexDirection: "row",
        border: "1px solid grey",
        borderRadius: 16,
    },
    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;
import React from "react";
import Notification from "./Notification";

const reservedNotifications = [
    {
        message: "안녕하세요 오늘의 일정을 알려드리겠습니다.",
    },
    {
        message: "점심식사 시간입니다.",
    },
    {
        message: "이제 곧 미팅이 시작됩니다.",
    },
];

var timer;

class NotificationList extends React.Component {
    constructor(props) {
        super(props);
        
        // 처음 빈 배열을 넣어 초기화
        this.state = {
            notifications: [],
        };
    }

    // setInterval을 이용하여 1초마다 정해진 작업
    // 미리 만들어둔 알림 데이터 배열 reservedNodification 로 부터
    // 알림 데이터를 하나씩 가져와서 state에 있는 notifications 배열에 넣고 업데이트
    componentDidMount() {
        const { notifications } = this.state;
        timer = setInterval(() => {
            if (notifications.length < reservedNotifications.length) {
                const index = notifications.length;
                notifications.push(reservedNotifications[index]);
                this.setState({ // state 업데이트를 위해 setState
                    notifications: notifications,
                });
            } else {
                clearInterval(timer);
            }
        }, 1000);
    }

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

export default NotificationList;

 

constructor는 일종의 생성자라고 보면 된다. 먼저 state()로 초기화 한 후, setInterval을 통해 배열에 원하는 데이터값을 하나씩 넣으면서 setState()로 업데이트를 한다. state()로는 업데이트를 하면 안된다.

 

 

 

1초에 하나씩 메세지가 생기는 걸 확인할 수 있다.

 

 

 

 

Lifecycle 실습

class Notification extends React.Component {
    constructor(props) {
        super(props);
        
        this.state = {};
    }

    componentDidMount() {
        console.log(`${this.props.id} componentDidMount() called.`);
    }

    componentDidUpdate() {
        console.log(`${this.props.id} componentDidUpdate() called.`);
    }

    componentWillUnmount() {
        console.log(`${this.props.id} componentWillUnmount() called.`);
    }

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

생명 주기를 확인할 수 있는 함수를 작성한다. componentDidmount()는 마운트 되었을 시, componentDidUpdate()는 컴포넌트가 업데이트 되었을 시, componentWillUnmount()는 컴포넌트가 언마운트 되었을 시에 실행된다.

 

 

 

 

const reservedNotifications = [
    {
        id: 1,
        message: "안녕하세요 오늘의 일정을 알려드리겠습니다.",
    },
    {
        id: 2,
        message: "점심식사 시간입니다.",
    },
    {
        id: 3,
        message: "이제 곧 미팅이 시작됩니다.",
    },
];


/ .... /


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

헷깔리지 않도록 메세지에 id 값을 주고, 렌더링 시, Notification에 키와 아이디값을 요소에 설정한다.

 

 

그럼 다음과 같이 터미널의 콘솔창에 나타난다. 첫번째 메세지가 마운트되고, 2번째 메세지가 마운트되어 나타나면 첫번째 메세지도 업데이트 된다. 3번째 메세지가 마운트되어 나타나면, 1,2번 메세지 역시 업데이트된다.

 

 

 

 

    componentDidMount() {
        const { notifications } = this.state;
        timer = setInterval(() => {
            if (notifications.length < reservedNotifications.length) {
                const index = notifications.length;
                notifications.push(reservedNotifications[index]);
                this.setState({ // state 업데이트를 위해 setState
                    notifications: notifications,
                });
            } else {
                this.setState({
                    notifications: [],
                });
                clearInterval(timer);
            }
        }, 1000);
    }

but 컴포넌트가 언마운트되었을 시, componentWillUnmount() 함수를 실행하는데, 위의 작업은 componentWillUnmount()가 실행되지 않았다. 그 이유는 언마운트 된 컴포넌트가 없기 때문이다. 그래서 모든 업데이트가 끝난 후, 부분에 setState() 로 notifications 배열을 빈 배열로 만든다.

 

 

 

모든 작업 이후, notifications 배열이 빈 배열로 바뀌었기 때문에, 1,2,3번 메세지는 언마운트된다. 콘솔창에서 확인할 수 있다.

'React' 카테고리의 다른 글

[React] Hooks 실습  (0) 2024.08.07
[React] Hooks  (0) 2024.08.06
[React] Components 와 Props  (0) 2024.08.01
[React] Rendering Elements  (0) 2024.07.31
[React] JSX 의 장점과 사용법  (0) 2024.07.30

Components

작은 컴포넌트들이 모여서 하나의 컴포넌트를 구성하고 이런 컴포넌트들로 전체 페이지가 구성됨
Component가 붕어빵 틀이라면 Element는 붕어빵. 자바의 객체와도 같음

 

Props

prop은 리액트 Component의 property(속성)
붕어빵에 들어가는 재료라고 보면 됨

Props 의 특징
읽기 전용 - 값을 변경할 수 없다. (붕어빵 다 구워졌으니 속재료 변경 불가)
같은 Props에 대해서는 항상 같은 결과를 보여줌

Props 사용법
JSX 사용경우 (권장)

 


JSX 사용하지 않을 경우

 

 

 

Component 만들기

 

 

class component : React.Component를 상속받음

 


Component의 이름 짓는 법
- Component의 이름은 항상 대문자로 시작해야 한다!

Component 렌더링

 


Component 합성
- Component 안에 또 다른 Component


Component 추출
- 큰 Component 를 일부를 추출해서 새로운 Component를 만듦
- 재사용성 높아짐
- 개발속도 향상

 

 

실습 : 댓글 컴포넌트 만들기

import React from "react";

function Comment(props) {
    return (
        <div>
            <h1>제가 만든 첫 컴포넌트</h1>
        </div>
    );
}

export default Comment;
import React from "react";
import Comment from "./Comment";

function CommentList(props) {
    return (
        <div>
            <Comment />
        </div>
    );
}

export default CommentList;
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

import CommentList from './chapter_05/CommentList';

setInterval(() => {
  ReactDOM.render(
    <React.StrictMode>
    <CommentList />
    </React.StrictMode>,
    document.getElementById('root')
  );
}, 1000);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

댓글 리스트 컴포넌트 안에 댓글 컴포넌트가 포함되어 있는 형태이다.

 

 

 

컴포넌트에 스타일 입히기

import React from "react";

const styles = {
    wrapper: {
        margin: 8,
        padding: 8,
        display: "flex",
        flexDirection: "row",
        border: "1px solid grey",
        borderRadius: 16,
    },
    imageContainer: {},
    image : {
        width: 50,
        height: 50,
        borderRadius: 25,
    },
    contentContainer: {
        marginLeft: 8,
        display: "flex",
        flexDirection: "column",
        justifyContent: "center",
    },
    nameText: {
        color: "black",
        fontSize: 16,
        fontWeight: "bold",
    },
    commentText: {
        color: "black",
        fontSize: 16,
    },
};

function Comment(props) {
    return (
        <div style={styles.wrapper}>
            <div style={styles.imageContainer}>
                <img 
                    src="https://lh3.googleusercontent.com/proxy/fKYexLyW7D6DMKEsN_TZbTFFgn95uCZDwkBW8-YSORGtmw9KrzayhocMeyh__7sl0rW9XjR0Qlp2gC5-Txx9IXPzsKZxvdoXB9OCROAg4tzP_XBvrA"
                    style={styles.image} 
                />
            </div>

            <div style={styles.contentContainer}>
                <span style={styles.nameText}>김이박</span>
                <span style={styles.commentText}>
                    제가 쓴 첫 댓글
                </span>
            </div>
        </div>
    );
}

export default Comment;

 

다음과 같이 Comment.jsx를 수정한다. 

 

 

 

그럼 다음과 같이 댓글에 스타일이 적용된 것을 확인할 수 있다.

 

 

 

 

컴포넌트에 Props 추가하기

            <div style={styles.contentContainer}>
                <span style={styles.nameText}>{props.name}</span>
                <span style={styles.commentText}>
                    {props.comment}
                </span>
            </div>

 

우선 위에서 수정한 Comment 컴포넌트에서 닉네임과 댓글 부분을 다음과 같이 {props.name} 그리고 {props.comment}로 수정한다.

 

 

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

function CommentList(props) {
    return (
        <div>
            <Comment name={"야야야"} comment={"hello world!"} />
            <Comment name={"좋아요"} comment={"ㅋㅋㅋㅋㅋㅋ"} />
        </div>
    );
}

export default CommentList;

 

CommentList 컴포넌트로 가서 Comment 컴포넌트들의 요소를 다음과 같이 설정하면...

 

올바르게 값들이 불려진 걸 확인할 수 있다.

 

 

 

Comment 데이터를 별도의 객체로 분리하기

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

const comments = [
    {
        name: "손흥민",
        comment: "안녕하세요, 손흥민입니다.",
    },
    {
        name: "이강인",
        comment: "이강인입니다. 반갑습니다.",
    },
    {
        name: "김민재",
        comment: "제 이름은 김민재입니다. 반갑습니다.",
    },
];

function CommentList(props) {
    return (
        <div>
            {comments.map((comment) => {
                return (
                    <Comment name={comment.name} comment={comment.comment} />
                );
            })}
        </div>
    );
}

export default CommentList;

CommentList.jsx 에서 각 댓글 객체를 작성하고, 컴포넌트에서 map으로 각 댓글 객체 조회 후, Comment 컴포넌트 소환 후 각 요소들에 데이터들을 집어 넣는다.

 

 

댓글 객체의 데이터들이 불려져왔다.

'React' 카테고리의 다른 글

[React] Hooks  (0) 2024.08.06
[React] State and Lifecycle  (0) 2024.08.03
[React] Rendering Elements  (0) 2024.07.31
[React] JSX 의 장점과 사용법  (0) 2024.07.30
[React] create-react-app  (0) 2024.07.29

+ Recent posts