Hooks

Goun Seo·2021년 12월 15일
0

Hooks ? class 컴포넌트의 state 관리와 라이프사이클 관리 기능을 함수형 컴포넌트에서도 가능하게 하는 함수들을 말한다.

class 컴포넌트와 함수형 컴포넌트의 차이
함수형 컴포넌트는 클래스 컴포넌트보다 선언하기 쉽고 메모리 자원도 클래스보다 덜 사용하나 state와
라이프사이클 api 사용이 불가능한 단점이 있다.
이 단점을 리액트 v.16.8 업데이트 이후 Hooks라는 기능이 도입 되면서 해결됨

클래스형 컴포넌트를 작성하다가 this 바인딩 때문에 발생
자바스크립트 상속 클래스에선 super()를 반드시 호출해줘야 한다. 이런 규칙에 따라 클래스형 컴포넌트에서도 super(props)를 호출
이런 단점을 해결 하기 위해 함수형 컴포넌트에서 state와 라이프사이클 메서드 관리 기능을 추가하자는 의견이 나오면서 Hooks가 나오게 됨

Hooks 장점

  • Hook은 props, state, context, refs, 그리고 lifecycle와 같은 React 개념에 좀 더 직관적인 API를 제공

  • class 사용을 위해서 JavaScript의 this키워드 작동 원리를 알아야
    하고 코드의 재사용성과 구성을 매우 어렵게 느껴저서 용자들은 props, state, 그리고 top-down 데이터 흐름을 완벽하게 하고도, Class의 이해에는 어려움이 있었음 이런 단점을 보완함


Hooks는 크게 두가지 종류가 있다. 1. 리액트 내장 Hooks 2. 커스텀
먼저 리액트 내장 Hooks를 살펴보자

useState : 함수형 컴포넌트에서 state를 사용할 수 있게 되면서 가변적인 상태를 지닐 수 있게 됨

import React,{useState} from 'react'
const Count = () => {
  const [value,setValue] = useState(0);
  return (
    <div>
    <p>
    현재 카운터 값은 <b>{value}</b>입니다. 
</p>
<button onClick={() => setValue(value +1)}>+1 </button>
<button onClick={() => setValue(value -1)}>-1 </button>
</div>
);
};
export default count;

useEffect: lifecycle 메서드 역할과 비슷함,

componentDidMount와 componentDidUpdate를 합친 형태로 보면됨
컴포넌트가 렌더링 될 때 마다 특정 작업을 수행하도록 설정

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

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

  // componentDidMount, componentDidUpdate와 비슷합니다
  useEffect(() => {
    // 브라우저 API를 이용해 문서의 타이틀을 업데이트합니다
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

useReducer : useState보다 다양한 상태를 다른 값으로 업데이트 하고 싶을때 사용

import React, { useReducer } from "react";

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <>
      <h2>{state.count}</h2>
      <button onClick={() => dispatch({ type: "INCREMENT", step: 1 })}>
        증가
      </button>
      <button onClick={() => dispatch({ type: "DECREMENT", step: 1 })}>
        감소
      </button>
    </>
  );
}

useMemo: 함수형 컴포넌트에서 발생하는 연산을 최적화

// App.js

import Info from "./Info";

const App = () => {
  const [color, setColor] = useState("");
  const [movie, setMovie] = useState("");

  const onChangeHandler = e => {
    if (e.target.id === "color") setColor(e.target.value);
    else setMovie(e.target.value);
  };

  return (
    <div className="App">
      <div>
        <label>
          What is your favorite color of rainbow ?
          <input id="color" value={color} onChange={onChangeHandler} />
        </label>
      </div>
      <div>
        What is your favorite movie among these ?
        <label>
          <input
            type="radio"
            name="movie"
            value="Marriage Story"
            onChange={onChangeHandler}
          />
          Marriage Story
        </label>
        <label>
          <input
            type="radio"
            name="movie"
            value="The Fast And The Furious"
            onChange={onChangeHandler}
          />
          The Fast And The Furious
        </label>
        <label>
          <input
            type="radio"
            name="movie"
            value="Avengers"
            onChange={onChangeHandler}
          />
          Avengers
        </label>
      </div>
      <Info color={color} movie={movie} />
    </div>
  );
};

export default App;

const getColorKor = color => {
    console.log("getColorKor");
    switch (color) {
      case "red":
        return "빨강";
      case "orange":
        return "주황";
      case "yellow":
        return "노랑";
      case "green":
        return "초록";
      case "blue":
        return "파랑";
      case "navy":
        return "남";
      case "purple":
        return "보라";
      default:
        return "레인보우";
    }
  };

  const getMovieGenreKor = movie => {
    console.log("getMovieGenreKor");
    switch (movie) {
      case "Marriage Story":
        return "드라마";
      case "The Fast And The Furious":
        return "액션";
      case "Avengers":
        return "슈퍼히어로";
      default:
        return "아직 잘 모름";
    }
  };


const Info = ({ color, movie }) => {
  const colorKor = getColorKor(color);
  const movieGenreKor = getMovieGenreKor(movie);

  return (
    <div className="info-wrapper">
      제가 가장 좋아하는 색은 {colorKor} 이고, <br />
      즐겨보는 영화 장르는 {movieGenreKor} 입니다.
    </div>
  );
};

export default Info;



import React, { useMemo } from "react";

const colorKor = useMemo(() => getColorKor(color), [color]);
const movieGenreKor = useMemo(() => getMovieGenreKor(movie), [movie]);

useCallback: Hook을 사용하면 만들어놨던 함수를 재사용, 메모리제이션된 함수를 반환한다라는 문장이 핵심

import React, { useState, useCallback } from "react";

const onChangeHandler = useCallback(e => {
    if (e.target.id === "color") setColor(e.target.value);
    else setMovie(e.target.value);
  }, []);

useRef :함수형 컴포넌트에서 ref를 쉽게 사용 할수 있게 함 ,DOM을 직접 참조하기 위해 사용

import { useState, useRef } from "react";

export default function UseRefSample1() {
  const countRef = useRef(null);
  const [Count, setCount] = useState(0);

  return (
    <div className="wrap">
        <h2>StopWatch 만들기</h2>
        <div>
            <div className="timer">Timer: {Count}ms</div>
            <button>Start</button>
            <button>Stop</button>
            <button>Reset</button>
        </div>
    </div>
  );
}

const startHandler = () => {
    countRef.current = setInterval(() => setCount((c) => c + 1), 100);
  };

  const stopHandler = () => {
    clearInterval(countRef.current);
    countRef.current = null;
  };

  const resetHandler = () => {
    stopHandler();
    setCount(0);
  };

import { useEffect, useRef, useState } from "react";

export default function UseRefSample2() {
  const [Msg, setMsg] = useState(null);
  return (
    <div className="wrap">
      <h2>Input Focus</h2>
      <p>
        <input placeholder="Name" />
      </p>
      <p>
        <input placeholder="Age" />
      </p>
      <button>Submit</button>
      {
          Msg === null ? "" 
          : Msg ?  <div className="msg">모두 입력되었습니다!</div> 
          : <div className="msg warning">모두 입력하세요!</div>
      }
    </div>
  );

Hooks 사용 규칙

  1. 최상위(at the top level)에서만 Hook을 호출해야 합니다. 반복문, 조건문, 중첩된 함수 내에서 Hook을 실행하지 마세요.
    React 함수 컴포넌트 내에서만 Hook을 호출해야 합니다. 일반

  2. JavaScript 함수에서는 Hook을 호출해서는 안 됩니다. (Hook을 호출할 수 있는 곳이 딱 한 군데 더 있습니다. 바로 직접 작성한 custom Hook 내입니다. 이것에 대해서는 나중에 알아보겠습니다.)


커스텀 Hooks

개발을 하다 보면 가끔 상태 관련 로직을 컴포넌트 간에 재사용하고 싶은 경우. Custom Hook은 이들 둘과는 달리 컴포넌트 트리에 새 컴포넌트를 추가하지 않고도 이것을 가능하게 함
이름이 ”use“로 시작하고, 안에서 다른 Hook을 호출

import { useState, useEffect } from 'react';

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}

https://ko.reactjs.org/docs/hooks-custom.html


clouser

클로저는 함수가 속한 렉시컬 스코프를 기억하여 함수가 렉시컬 스코프 밖에서 실행될 때에도 이 스코프에 접근할 수 있게 하는 기능

function useState(initialValue) {
  var _val = initialValue // _val은 useState에 의해 만들어진 지역 변수입니다.
  function state() {
    // state는 내부 함수이자 클로저입니다.
    return _val // state()는 부모 함수에 정의된 _val을 참조합니다.
  }
  function setState(newVal) {
    // 마찬가지
    _val = newVal // _val를 노출하지 않고 _val를 변경합니다.
  }
  return [state, setState] // 외부에서 사용하기 위해 함수들을 노출
}
var [foo, setFoo] = useState(0) // 배열 구조분해 사용
console.log(foo()) // 0 출력 - 위에서 넘긴 initialValue
setFoo(1) // useState의 스코프 내부에 있는 _val를 변경합니다.
console.log(foo()) // 1 출력 - 동일한 호출하지만 새로운 initialValue

참고: https://ko.reactjs.org/docs/hooks-overview.html
https://hewonjeong.github.io/deep-dive-how-do-react-hooks-really-work-ko/

profile
그리다 보면 ~ ♪

0개의 댓글