(TIL 14일차) React (4)

pks787·2020년 2월 11일
0

TIL(Today I Learned)

목록 보기
7/42

React Hook이란?

  • 클래스형 컴포넌트에서만 사용가능한 state, 라이프사이클 메소드를 함수형 컴포넌트에서도 사용가능하게 만들어주는 좋은 기능❗❗
  • 리액트 16.8v 부터 사용 가능

Hook의 특징

  • 선택적 사용
    기존의 리액트들은 대부분 클래스형 컴포넌트로 사용되어 왔기 때문에 기존 클래스형 컴포넌트들은 유지하되 선택적으로 Hook을 사용하면 됨
  • 이전버전과 100% 호환 유지
  • 기존 props,state,context,refs 기능보다 더 직관적인 API 제공
  • 로직에 기반을 둔 작은 함수로 컴포넌트를 나눔

사용 예시

import React, { useState } from 'react';

function Example() {
  // "count"라는 새로운 상태 값을 정의
  const [age, setAge] = useState(42); // age의 초기값을 42로
  const [fruit, setFruit] = useState('banana'); // fruit의 초기 값을 "banana"로
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
  // text의 초기 값을 Learn Hooks로
 
 //  한번에 여러개 선언 및 할당 가능
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
  • 기존의 클래스형 state를 this.setState로 변경하면 변경된 값을 기존 값에 덮어씌우는 방식이지만 Hook에서는 덮어씌우지 않고 새로운 값만 사용

useRef의 기능

특정 DOM 선택

import React, { useState, useRef } from 'react';

function InputSample() {
  const [inputs, setInputs] = useState({
    name: '',
    nickname: ''
  });
  const nameInput = useRef(); // ref 선언

  const { name, nickname } = inputs; // 비구조화 할당을 통해 값 추출

  const onChange = e => {
    const { value, name } = e.target; // 우선 e.target 에서 name 과 value 를 추출
    setInputs({
      ...inputs, // 기존의 input 객체를 복사한 뒤
      [name]: value // name 키를 가진 값을 value 로 설정
    });
  };

  const onReset = () => {
    setInputs({
      name: '',
      nickname: ''
    });
    nameInput.current.focus(); // 해당 input에 focus 적용
  };

  return (
    <div>
      <input
        name="name"
        placeholder="이름"
        onChange={onChange}
        value={name}
        ref={nameInput} // ref 적용
      />
      <input
        name="nickname"
        placeholder="닉네임"
        onChange={onChange}
        value={nickname}
      />
      <button onClick={onReset}>초기화</button>
      <div>
        <b>: </b>
        {name} ({nickname})
      </div>
    </div>
  );
}

export default InputSample;
  • 기존 JS에서는 getElementbyId 등으로 접근
  • 가끔 DOM을 직접 선택하는 경우가 발생(특정 엘리먼트 크기, 스크롤바 위치 등)
  • useUef라는 Hook 함수 사용해서 접근
  • ref의 current 객체는 현재 ref 값으로 설정된 input에 접근

컴포넌트 안에서 조회 및 수정하는 변수 관리

 const nextId = useRef(4);
  • useRef 로 관리하는 변수는 값이 바뀐다고 해서 컴포넌트가 리렌더링되지 않음
  • 리액트는 컴포넌트에서의 상태는 상태를 바꾸는 함수를 호출한 뒤 그 다음 렌더링 이후로 업데이트 된 상태를 조회하지만, useRef 로 관리하고 있는 변수는 설정 후 바로 조회 가능

사용하는 곳

  • setTimeout, setInterval 을 통해서 만들어진 id
  • 외부 라이브러리를 사용하여 생성된 인스턴스
  • scroll 위치

Effect Hook

import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // componentDidMount, componentDidUpdate와 비슷
  useEffect(() => {
    // 브라우저 API를 이용해 문서의 타이틀을 업데이트
    document.title = `You clicked ${count} times`;
  });
 const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
  • 다른 컴포넌트에 영향을 주며, 렌더링 과정에서 구현 불가능한 작업을 side effects(effects)라 하는데 Effect Hook(useEffect)를 사용하면 side effects를 가능하게 만들어 줌 ❗❗
  • 본인 render()를 실행하는게 주 임무
  • 본인 컴포넌트 이외의 이벤트를 side effects라 함
  • 클래스형 컴포넌트의 라이프 사이클 메소드의 목적으로 제공되지만 이것들을 하나의 API로 묶음
  • 기본적으로 매 렌더링 이후에 effects 실행(첫 번째 포함)
  • 해제 하고 싶을 시 해제하는 함수를 반환해주면 됨(선택사항)
  • Hook을 사용하면 구독을 추가하고 제거하는 로직과 같이 서로 관련 있는 코드들을 한군데에 모아서 작성(기존 클래스형 라이프 사이클 메소드랑 다르게)

useEffect 발생 작업

function User({ user, onRemove, onToggle }) {
  useEffect(() => {
    console.log('컴포넌트가 화면에 나타남');
    return () => {
      console.log('컴포넌트가 화면에서 사라짐');
    };
  }, []); 
  // 첫번째 파라미터는 함수, 두번째 파라미터는 의존값이 들어있는 배열(deps)
function User({ user, onRemove, onToggle }) {
  useEffect(() => {
    console.log('user 값이 설정됨');
    console.log(user);
    return () => {
      console.log('user 가 바뀌기 전..');
      console.log(user);
    };
  }, [user]);


useEffect 구조❗❗

  • 첫 번째 콜백 함수 : ComponentDidMount & ComponentDidUpdate 역할 (여러개 정의 가능)
  • return 값 : ComponentWillUnMount(Clean Up) 역할

마운트 시 주로 하는 작업

  • props 로 받은 값을 컴포넌트의 로컬 상태로 설정
  • 외부 API 요청 (REST API 등)
  • 라이브러리 사용 (D3, Video.js 등...)
  • setInterval 을 통한 반복작업 혹은 setTimeout 을 통한 작업 예약

언마운트 시 주로 하는 작업

  • setInterval, setTimeout 을 사용하여 등록한 작업들 clear 하기 (clearInterval, clearTimeout)
  • 라이브러리 인스턴스 제거

deps 존재유무에 따른 발생 작업

[]안에 값이 존재

  • 안에 기입한 값이 변경 될 때만❗ 첫 번째 콜백 함수 호출
  • componentDidmount , componentDidUpdate(변경 및 변경직전 2개 포함), return(cleanup) 호출 => 총 4군데
  • 컴포넌트가 처음 마운트 될 때 호출, 언마운트 시 호출
  • useEffect 안에 사용하는 state나 props가 있으면 deps안에 넣어줘야 하는 것이 규칙❗❗
  • 넣지 않으면 useEffect가 실행 될 때 최신 state/props 상태를 가르키지 못함

[]안에 값 생략 시

  • componentDidmount만 하고 싶은 경우 (처음 나타날 때 딱 한번만 호출)
  • component가 사라질 때 return(cleanup) 함수 호출
  • 부모의 state가 변경 됨에 따라 자식들도 전부 렌더링 되는 문제❗

[]을 작성 안할 시

  • 컴포넌트가 리렌더링 할 때마다 호출
  • 리액트는 기본적으로 부모컴포넌트가 리렌더링되면 자식은 무조건 리렌더링(변경사항 없어도)
  • 실제 DOM에 변화가 반영되는 것은 바뀐 내용이 있는 컴포넌트
  • But, Virtual DOM에서는 모두 다 리렌더링 중이기에 컴포넌트를 최적화 시키면 Virtual DOM에서 렌더링 하는 자원 소모를 줄일 수 있음

profile
Front End. Dev

0개의 댓글