React Hooks

Hooks는 리액트 v16.8에 새로 도입된 기능으로서, 함수형 컴포넌트에서도 상태 관리를 할 수 있는 useState, 렌더링 직후 작업을 설정하는 useEffect 등의 기능등을 제공하여 기존의 함수형 컴포넌트에서 할 수 없었던 다양한 작업을 할 수 있게 해준다.

React Hooks 사용규칙

  1. 같은 Hooks을 여러번 호출 할 수 있다.
  2. 리액트 함수의 최상위에서만 호출해야 합니다.
    함수 컴포넌트 몸통이 아닌, 몸통 안 복합 실행문의 {}에서는 사용 할 수 없다.
    -> javascript의 block scope는 block 외에서 사용할 수 없기 때문이다.
if(counter) {
    const [sample, setSample] = useState(0);
}

useState를 조건문 안에서 호출하면 안된다는 Error

  1. 오직 리액트 함수 내에서만 사용되어야 합니다.
    이는 리액트 함수형 컴포넌트나 커스텀 Hook이 아닌 다른 일반 javaScript 함수 안에서는 호출이 안된다는 의미이다.
window.onload = function () {
    useEffect(() => {
        // do something...
    }, [counter]);
}

window.onload라는 함수에서 useEffect를 호출하면 안 된다는 Error

React Hooks 요약정리

  1. useState
    컴포넌트의 state(상태)를 관리 할 수 있다.
    상태에 따라, 다른 화면 출력
  2. useEffect
    렌더링 이후에 실행할 코드를 만들 수 있다.
    어떤 변수가 변경될때마다(의존성 배열), 특정기능이 작동하도록 할 수 있다.
  3. useContext
    부모컴포넌트와 자식컴포넌트 간의 변수와 함수를 전역적으로 정의할 수 있다.
  4. useReducer
    state(상태) 업데이트 로직을 reducer 함수에 따라 분리 할 수 있다.
  5. useRef
    컴포넌트나 HTML 요소를 레퍼런스로 관리할 수 있다.
  6. forwardRef
    useRef로 만든 레퍼런스를 상위 컴포넌트로 전달할 수 있다.
  7. useImperativeHandle
    useRef로 만든 레퍼런스의 상태에 따라, 실행할 함수를 정의 할 수 있다.
  8. useMemo, useCallback
    의존성 배열에 적힌 값이 변할 때만 값,함수를 다시 정의할 수 있다.(재랜더링 시 정의 안함)
  9. useLayoutEffect
    모든 DOM 변경 후 브라우저가 화면을 그리기(render)전에 실행되는 기능을 정할 수 있다.
  10. useDebugValue
    사용자 정의 Hook의 디버깅을 도와준다.

1. useState

1-1) state란?

react에서 사용자의 상태에 따라, 화면을 바꿔주기 위해 사용되는 트리거 역할을 하는 변수
react의 state를 감시하고, 바뀐 정보에 따라 화면을 표시해준다.
(state와 setState의 반환값을 비교)

1-2) useState 사용

// React에 기본적으로 내장되어 있는 useState 훅을 사용하면, state를 만들 수 있다.

import { useState } from "react";

// const [state, state변경함수] = useState(기본 state값);

const [isLoggedIn, setIsLoggedIn] = useState(false);

// 전에 만든 "isLoggedIn" state의 값을 true로 변경한다.

setIsLoggedIn(true);

// ** useState함수를 사용해 만든 "state 변경 함수"를 사용하여야 한다.

2. useEffect

2-1) useEffect란?

리액트 컴포넌트가 랜더링 될 때마다 특정 작업을 수행하도록 설정할 수 있다.

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

const Info = () => {
  const [name, setName] = useState('');
  const [nickname, setNickname] = useState('');
  
  useEffect(() => {
    console.log('렌더링이 완료되었습니다!');
    console.log({
      name,
      nickname
    });
  });

  const onChangeName = e => {
    setName(e.target.value);
  };

  const onChangeNickname = e => {
    setNickname(e.target.value);
  };

  return (
    (...)
  );
};

export default Info;

2-2) 마운트 될 때만 실행하고 싶을 때

만약 useEffect에서 설정한 함수가 컴포넌트가 화면에 처음 랜더링 될 때만 실행되고 업데이트 할 경우에는 실행 할 필요가 없는 경우엔 함수의 두번째 피라미터로 빈 배열을 넣으면 된다.

 useEffect(() => {
    console.log('마운트 될 때만 실행됩니다.');
  }, []);

2-3) 특정 값이 업데이트 될 때만 실행하고 싶을 때

   useEffect(() => {
    console.log(name);
  }, [name]);

배열 안에는 useState을 통해 관리하고 있는 상태를 넣어줘도 되고, props로 전달받은 값을 넣어줘도 된다.

2-4) useState와 useLayoutEffect의 차이

useEffect : 비동기 방식
useLayoutEffect : 동기 방식 (끝날때까지 react가 기다려줌)

3. useContext

3-1. 새로운 context 만들기

// newContext.js

import { createContext } from "react"  // createContext 함수 불러오기

// context안에 homeText란 변수를 만들고, 공백("") 문자를 저장한다.
const newContext = createContext({
  homeText: "",
})

3-2. context 사용할 부분 선택, 새로운 정보 저장

// App.js

import React from "react";
import Home from "./Home"; // 자식 컴포넌트 불러오기

import { newContext } from "./newContext"; // context 불러오기

export default function App() {

  // context에 저장할 정보를 입력한다.
  const homeText = "is Worked!"

  // NewContext.Provider로 우리가 만든 context를 사용할 부분을 감싸준다.
  return (
        <newContext.Provider value={{ homeText }}>
            <Home 
        </newContext.Provider>
  );
}

3-3. context 사용하기

// Home.js

import React from "react";
import { useContext } from "react";
import { newContext } from "../newContext";


export default function Home() {

  // useContext hook 사용해서, newContext에 저장된 정보 가져오기
  const { homeText } = useContext(newContext);
  
  // 불러온 정보 사용하기!!
  return (
    <React.Fragment>
      <p>{homeText}<p>
    </React.Fragment>
  );
}

3-4. context 사용시 좋은점

리액트에서 context 없이 변수나 함수를 다른 컴포넌트로 전달하려면 부모자식간에만 전달이 가능하므로, 컴포넌트가 많아질수록 불필요한 컴포넌트를 많이 거쳐야한다. (props drilling 발생)
context를 이용하면 중간과정을 재치고 직접적으로 전달 가능

3-4. context 사용시 주의점

전달하고자하는 컴포넌트에 context를 만들면 불필요한 호출이 추가될 수 있으므로 context 전용 파일을 만들어야 한다. (전역변수는 통상적으로 store 라는 폴더에 저장해서 관리)

4.useReducer

useState는 useState보다 컴포넌트에서 더 다양한 상황에 따라 다양한 상태를 다른 값으로 업데이트 해주고 싶을 때 사용한다.
리듀서라는 개념은 Redux를 배우게 될 때 더 자세히 알아 보자.

4-1) useReducer 정의

리듀서는 현재 상태와, 업데이트를 위해 필요한 정보를 담은 액션(action)값을 전달 받아 새로운 상태를 반환하는 함수이다.
리듀서 함수에서는 새로운 상태를 만들 때는 꼭 불변성을 지켜주어야 한다
상태를 업데이트하는 로직을 컴포넌트 밖에 작성해야한다

function reducer(state, action) {
  return { ... }; // 불변성을 지키면서 업데이트한 새로운 상태를 반환합니다
}

액션값은 주로 다음과 같은 형태로 이루어져있다.

{
type: 'INCREMENT',
// 다른 값들이 필요하다면, 추가적으로 들어감
}

Redux에서는 액션 객체에서는 어떤 액션인지 알려주는 type 필드가 꼭 있어야 하지만, useReducer에서 사용하는 액션 객체는 꼭 type를 지니고 있을 필요가 없다. 심지어 객체가 아닌 문자열이나, 숫자여도 상관이 없다.

4-2) useReducer 사용 예시

// dispatch에게 인자를 전달받음
function reducer(state, action){
  // state에 이전 상태값 들어있음
  // action에 {키:값, ...} 형식의 객체(action)가 들어있음
  switch (action.type) {
    case 'INCREMENT':
      return { value: state.value + 1 };
    case 'DECREMENT':
      return { value: state.value - 1 };
    default:
      // 아무것도 해당되지 않을 때 기존 상태 반환
      return state;
  }
}

 const Counter = () => {
     // 1번째 : reducer 함수, 2번째 : 초기 상태값
  const [state, dispatch] = useReducer(reducer, { value: 0 });

  return (
    <div>
      <p>
        현재 카운터 값은 <b>{state.value}</b> 입니다.
      </p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>+1</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>-1</button>
    </div>
  );
};

export default Counter;

5. useRef

5-1) useRef 사용

HTML요소(태그)나 컴포넌트의 메모리주소를 가져와, 객체(레퍼런스) 형식으로 관리할 수 있다.

  • current 속성을 가지고 있는 객체를 반환한다.
  • current 값이 바뀌어도, 재렌더링 되지 않는다.
  • 재렌더링시에도, current 값은 없어지지 않는다.

6. forwardRef

6-1) forwardRef 사용

부모 컴포넌트와, 자식 컴포넌트가 함수형 컴포넌트로 정의 되었을때,

부모 컴포넌트에서, 자식 컴포넌트를 객체 형식으로 관리할 수 있다.

// 자식 컴포넌트
import {forwardRef} from 'react'

// 부모 컴포넌트의 ref를 인자로 받아올 수 있다.
const _TextInput = (
  props,
  ref
) => {
  // 자식 컴포넌트의 ref에 useRef()객체 전달
  return (
    <div>
      <p>Hello</p>
      <TextInput
        ref={ref}
        {...props}
      />
    </div>
  );
};

// forwardRef()으로 감싸주기
export const MyTextInput = forwardRef(_TextInput);
// 부모 컴포넌트
import {useRef} from 'react'

const Parent = () => {
  const testRef = useRef(null);
  const setFocus = () => testRef.current?.focus();
  
  // 자식 컴포넌트에게 ref 전달
  return <ImperativeTextInput ref={testRef} />
};

7. useImperativehandle

7-1) useImperativehandle 사용

import {useRef, useImperativeHandle} from 'react'

const ImperativeTextInput = (props, ref) => {
  const textInputRef = useRef<TextInput | null>(null);
  
  // ref을 전달받아, 메소드를 만들어준다. ( 다른 ref 호출 가능 )
  useImperativeHandle(
    ref,
    () => ({
      focus() {
        textInputRef.current?.focus();
      },
      dismiss() {
        Keyboard.dismiss();
      },
    }),
    [],
  );
  return <TextInput ref={textInputRef} {...props} />;
};

export default forwardRef(ImperativeTextInput);

8. useMemo

8-1) useMemo 사용

Memoization : 과거에 계산한 값을 반복해서 사용할때, 그 값을 캐시에 저장하는 것

  • 재귀함수에 사용하면, 불필요한 반복 수행을 대폭 줄일 수 있다.
  • 의존성을 가지므로, 첫 렌더링시에만 저장할 변수를 설정할 수 있다.
export default function App(){
  const data = useMemo(()=> "data",[]);
  // 데이터 변수는 의존성 배열 []에따라 선언된다. ( []사용시, 첫 렌더링 시에 1번만 선언 )
  return <></>
}

9. useCallback

9-1) useMemo 사용

useMemo의 함수버전. 반복적으로 사용되는 함수를 캐시에 저장할 수 있다.

export default function App(){
  const avatarPressed = useCallback(() => Alert.alert('avatar pressed.'), []);
  return <></>
}

cache

React.js는 컴포넌트 내부에서 전역변수를 사용할 수 있도록 cache 객체 안에, key:value 형식으로 저장하는 방식을 사용한다.

// 전역적인 캐시 생성
const cache: Record<string, any> = {};

export default function createOrUse<T>(key: string, callback: () => T) {
  if (!cache[key]) {
    cache[key] = callback();
    // 키 : 값 저장
  }
  return cache[key];
  // 입력한 키에 해당하는 값 반환
}

의존성

React.js의 캐시는, 상황에 따라 업데이트하도록 만드는 의존성 배열을 만들 수 있다.

( 배열 속의 값이 바뀔때마다, 캐시를 업데이트 한다. )

useEffect(() => {
  console.log("dependency")
}, [의존성1, 의존성2 ...])

/*
  1. 의존성 변경
  2. cache내부에서, 의존성에 해당하는 key 찾아서 업데이트
  3. React가 컴포넌트를 재렌더링함
*/

다른 Hooks

  1. custom hook
    만약에 여러 컴포넌트에서 비슷한 기능을 공유하게 되는 일이 발생한다면 이를 우리들만의 hook을 작성하여 로직을 재사용 할 수 있다.
  2. react hooks 라이브러리
  • react-hook-form
  1. 다른 개발자들이 만든 다양한 hooks 리스트
profile
말에 힘이 있는 사람이 되기 위해 하루하루, 성장합니다.

0개의 댓글