React Hook

박민형·2022년 12월 23일
1
post-thumbnail

📌 React Hook을 알아본 이유

  • 여러 Hooks 별로 어떤 기능을 수행하는지 정확히 알지 못하고 있서서
  • 웹 사이트를 제작함에 있어 Hook에 대해서 제대로 알아야 상황에 맞게 적절한 Hook을 사용할 수 있기 때문에

📌 React Hook 이란?

  • 훅은 클래스 컴포넌트에서만 가능했던 state, ref 등 리액트의 핵심적인 기능을 함수에서도 가능하게 만듬
  • 클래스 컴포넌트보다 간결할게 작성할 수 있어 훅이 등장한 이래로 대부분의 리액트 컴포넌트는 함수 컴포넌트로 작성

📌 React Hook이 필요한 이유

클래스 컴포넌트 문제

  • 클래스 개념 어려움
  • 클래스형 컴포넌트의 복잡함
  • 컴포넌트 사이에서 상태 로직 재사용의 어려움

초기 함수형 컴포넌트 문제

  • 클래스 형 컴포넌트는 렌더링할 때 render 함수만 호출 되므로 나머지 method 및 state는 보존
  • 함수형 컴포넌트는 함수안에 모든 로직이 다시 실행되므로 기존의 state 값을 보존하지 못하는 문제 발생 => 함수형 컴포넌트가 리렌더링될 때 무조건 새롭게 선언, 초기화, 할당 과정이 수행된다.
// class 형
class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      name: "park",
      age: 7,
    };
  }

  render() {
    return (
      <>
        <div>{this.state.name}</div>
        <div>{this.state.age}</div>
      </>
    );
  }
}
// 함수형
function App() {
  const state = {
    name: "park",
    age: 7,
  };

  return (
    <>
      <div>{state.name}</div>
      <div>{state.age}</div>
    </>
  );
}
  • Hook의 등장으로 브라우저에 메모리를 할당해 state를 관리 할 수 있도록 함 => useState

📌 React Hooks의 종류

useState

  • 함수형 컴포넌트에서 state 관리 및 사용 가능하도록 함
const Count = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <div>{count}</div>
      <button onClick={() => setCount((prev) => prev + 1)}>+</button>
      <button onClick={() => setCount((prev) => prev - 1)}>-</button>
    </div>
  );
};

useEffect

  • useEffect는 클래스 컴포넌트의 componentDidMount, ComponentDidUpdate, componentWillUnmount 라이프 싸이클 메소드와 비슷한 역할은 하지만 다른 개념이다.
  • 두번째 전달인자에 아무것도 넣지 않으면 re-render 될 때 마다 effect 수행, 빈 배열을 넣으면 컴포넌트가 마운트될 때만 콜백함수 호출, 배열안에 state 관련 변수를 넣게 되면 해당 state가 변할 때만 콜백함수 호출
  • clean-up 함수를 통해 이전 콜백 함수의 호출에 의해 특정 이벤트 및 DOM을 참조 하고 있는 경우 이벤트 제거 및 DOM 참조를 제거할 수 있다.
const Count = () => {
  const [count, setCount] = useState(0);
  const [name, setName] = useState("park");

  useEffect(() => {
    console.log("hi");
    document.title = `업데이트 횟수:${count}`;
  }); // 두 번째 전달 인자에 [count] 입력하면 count state가 수정되었을 때만 effect 호출

  const onClickIncrement = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <div>{count}</div>
      <button onClick={onClickIncrement}>증가</button>
      <div>{name}</div>
      <button onClick={() => setName(name === "park" ? "lee" : "park")}>
        이름 변경
      </button>
    </div>
  );
};

useContext

  • 전역으로 props 및 함수를 필요한 곳에만 사용할 수 있도록 하는 Hook
// context 생성
import { createContext } from "react";

export const NameContext = createContext(null);
// context Provider 설정 및 value 전달
import Ex from "./Ex";
import { NameContext } from "./useContext";


function App() {
  const introduce = {
    name: "park",
    age: 27,
  };
  
  return (
    <NameContext.Provider value={introduce}>
      <Ex />
    </NameContext.Provider>
  );
}

export default App;
// context 사용
import { useContext } from "react";
import { NameContext } from "./useContext";

const Ex = () => {
  const { name, age } = useContext(NameContext);
  return (
    <div>
      <div>{name}</div>
      <div>{age}</div>
    </div>
  );
};

export default Ex;

useCallback

  • 렌더링 최적화를 위해 사용
  • React 특성상 re-render 될 때마다 함수를 선언한 곳에서 함수가 새로 만들어 지기 때문에 새로 만들어진 함수로 하위 컴포넌트가 re-render되는 문제를 예방
  • dependecy에 빈 배열을 사용하게 되면 초기 함수를 계속 가지고 있으므로 업데이트 확인을 위해서 함수를 호출했을 때 변경된 state를 볼 수 없음 => update 된 값을 확인하고 싶다면 dependency에 state 변수를 입력해서 해당 state가 변경될 때만 함수가 새롭게 만들어지도록 구현
  • 만약 useCallback의 dependency배열에 들어가는 state가 re-render 될 때 마다 update되는 데이터라면 굳이 사용하지 않아도 됨
function App() {
  const [count, setCount] = useState(0);
  const noticeCount = () => {
    console.log("count Up!");
  };
  
  const noticeCount = useCallback(() => {
    console.log(count);
  }, []);
  
  const noticeCount = useCallback(() => {
    console.log(count);
  }, [count]);

  useEffect(() => {
    console.log("re-render");
  }, [noticeCount]);

  return (
    <>
      <div>
        <div>{count}</div>
        <button onClick={() => setCount((prev) => prev + 1)}>+</button>
        <button onClick={() => setCount((prev) => prev - 1)}>-</button>
        <button onClick={noticeCount}>카운터 값 확인</button>
      </div>
    </>
  );
}

useMemo

  • 성능 최적화를 위해 사용
  • 첫 번째 매개변수로 설정한 콜백함수의 return 값을 저장하고 있다가 두번째 매개변수로 설정한 값이 수정되지 않으면 재사용
  • dependency 배열에 설정한 값이 수정되면 값을 초기화
  • dependency 배열을 빈 배열로 설정하면 콜백함수 내부에서 사용되고 있는 상태값은 초기값을 참조
  • dependency 배열에 의존성 데이터를 설정하면 해당 데이터가 업데이트되면 콜백함수 내부에서 업데이트 된 의존성 데이터를 기반으로 다시 계산, 반환, 메모이제이션
function App() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState("park");

  const noticeCount = `${count} ${name}`;

  const noticeCount = useMemo(() => `${count} ${name}`}, []);

  const noticeCount = useMemo(() => `${count} ${name}`, [count]);

  useEffect(() => {
    console.log("re-render");
    console.log(noticeCount);
  }, [noticeCount]);

  return (
    <>
      <div>
        <div>{count}</div>
        <button onClick={() => setCount((prev) => prev + 1)}>+</button>
        <button onClick={() => setCount((prev) => prev - 1)}>-</button>
        <button
          onClick={() => setName((prev) => (prev === "park" ? "lee" : "park"))}
        >
          이름 변경
        </button>
      </div>
    </>
  );
}

useRef

  • DOM에 접근할 수 있도록 하는 Hook
  • 접근시에 cuurent property 사용
  • rendring과 관련 없는 변수에 사용
// DOM 접근
function App() {
  const ref = useRef(null);

  const focusElement = () => {
    ref.current.focus();
  };

  return (
    <div>
      <input type="text" ref={ref} />
      <button onClick={focusElement}>포커스 시작</button>
    </div>
  );
}
// rendring과 관련 없는 변수에 사용
function App() {
  const [count, setCount] = useState(0);
  const intervalRef = useRef(0);

  useEffect(() => {
    // 좋은 코드는 아닌 것 같음
    // setCount를 해당 로직에서만 사용하면 문제가 없는데 다른 곳에서 사용하게되면 문제가 될 수 있음
    intervalRef.current = setInterval(() => setCount((prev) => prev + 1), 1000);
  }, []);

  const stopInterval = () => {
    clearInterval(intervalRef.current);
  };

  return (
    <div>
      <div>{count}</div>
      <button onClick={stopInterval}>Stop Count Up</button>
    </div>
  );
}

📌 Hook의 규칙

Hook 규칙
1. React 컴포넌트의 최상위(at the Top Level)에서만 Hook을 호출 => 렌더링 될 때마다 항상 동일한 순서로 Hook이 호출 되는 것을 보장해야 함
2. 함수형 컴포넌트 및 Custom Hook Hook에서 호출 가능
3. 반복문, 조건문 혹은 중첩된 함수 내에서 Hook을 호출하면 안됨
4. eslint-plugin-react-hooks 라는 ESLint 플러그인이 1, 2번 규칙을 강제하도록 함(CRA에 기본 설정 되어있음)

왜 반복문, 조건문 혹은 중첩된 함수 내에서 Hook을 호출하면 안될까?

  • React는 Hook이 호출되는 순서에 의존

📌 함수형 컴포넌트는 이전 상태를 어떻게 알고 있을까?

// useState(비슷한 형태)

let _value;

export useState(initialValue){
  if (_value === 'undefined') {
    _value = initialValue;
  }
  const setValue = newValue => {
    _value = newValue;
  }
  
  return [_value, setValue];
}
  • 클로저 이용
  • useState 바깥쪽에 state를 저장
  • 컴포넌트의 state들은 선언된 컴포넌트를 유일하게 구별할 수 있는 키로 접근할 수 있는 배열로 저장 되있음(순서대로 저장 되있음)
  • React Hook 규칙 위반 시 문제

📌 참고

React Hooks 총정리
https://velog.io/@goyou123/React-Hooks-%EC%B4%9D%EC%A0%95%EB%A6%AC/
클래스형과 함수형 차이
https://velog.io/@sdc337dc/0.%ED%81%B4%EB%9E%98%EC%8A%A4%ED%98%95-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8
프론트엔드 기술 저장소
https://k-developer.gitbook.io/dev/react/react-hook
useContext란? React hooks useContext란?
https://ko-de-dev-green.tistory.com/67
useContet 사용법 및 예제
https://itprogramming119.tistory.com/entry/React-useContext-%EC%82%AC%EC%9A%A9%EB%B2%95-%EB%B0%8F-%EC%98%88%EC%A0%9C
useState Hook과 클로저
https://velog.io/@ggong/useState-Hook%EA%B3%BC-%ED%81%B4%EB%A1%9C%EC%A0%80
공식문서
https://ko.reactjs.org/docs/hooks-intro.html

0개의 댓글