#1. Hooks

1. Hooks의 개념

1) Hooks란?

  • React가 16.8 버전으로 업데이트 되면서 새로 도입된 기능이다.
  • 클래스형 컴포넌트의 단점을 극복하고자 나온 함수형 컴포넌트에 대한 기능이다.
  • 즉, 함수 형태의 컴포넌트에서 사용되는 기술을 Hook이라 부른다.
    • 또는 함수형 컴포넌트로도 클래스형 컴포넌트의 기능을 사용할 수 있도록 해주는 기술을 Hook이라 부른다.

 

2) 함수형 컴포넌트를 사용하는 이유

  • 기존에 사용하던 클래스형 컴포넌트는 코드의 재사용성이 낮고, 코드 구성이 어려웠다. (클래스 문법이 어렵다)
    • 기본적인 JS 문법을 알아야 사용이 가능했다.
  • 클래스는 코드를 축소하기 힘들고, React의 최신 기술들이 클래스형 컴포넌트에 효과적으로 적용되지 않았다.
  • 이러한 클래스의 단점들을 함수형 컴포넌트로 커버할 수 있었지만, 함수형 컴포넌트는 state나 life cycle을 다루는 기술이 없었다.
  • 하지만, hook의 등장으로 state를 다룰 수 있게 되면서 함수형 컴포넌트의 사용률이 급격히 올라가게 되었다.

※ 클래스형 컴포넌트

// 클래스형 컴포넌트
import React, { Component } from 'react';

class App extends Component {
  render(){
    return (
      //jsx
    );
  };
};

export default App;

 

※ 함수형 컴포넌트

// 함수형 컴포넌트
import React from 'react';

function App(){ 
  return(
      //jsx
  );
};

export default App;

// 화살표 함수 표현도 가능하다.
const App = () => {};

 

3) Hook의 규칙

  • Hook은 반드시 최상위에서만 호출해야한다.
  • 반복문, 조건문 또는 중첩된 함수 내에서는 Hook을 실행할 수없다.
  • React Component 내에서만 호출해야 한다. (js에서는 안됨)

 

4) React에서 이벤트 구현시 주의할 점

  • 이벤트 리스너의 이름은 HTML 속성이 아닌 JSX에 의한 자바스크립트 프로퍼티 이므로 카멜표기법으로 작성해야한다.
    • ex) onclick → onClick
  • 이벤트 리스너에 전달할 이벤트 핸들러는 코드 형태가 아니라 반드시 함수 형태로 전달 해야한다.
    • ex) onClick(( ) => { ... } )

 

2. 기본 Hook 함수들

1) useState

  • 가장 기본적인 Hook 함수
  • 함수형 컴포넌트에서 state값을 생성한다. (이 페이지 안에서 유효한 전역변수 같은 개념)
  • 하나의 useState 함수는 하나의 값만 관리할 수 있다.
  • state 값은 직접 변경할 수 없고, 반드시 setter를 통해서만 변경이 가능하다.
  • 컴포넌트에서 관리해야 할 상태가 여러개라면 useState를 여러번 사용하면 된다.

 

  • useState( ) 함수를 import하고 사용하는 경우.
import React, {useState} from 'react';

const [상태변수 , 변수에대한setter함수] = useState(초기값);
  • useState( ) 함수를 import 하지 않고 직접 사용하는 경우 (결국 둘다 같은 방법이다)
import React from 'react';

const [상태변수 , 변수에대한setter함수] = React.useState(초기값);

 

※ 간단한 예제

- MyState.js

import React, { useState } from 'react';

const MyState = () => {
  // 초기값을 빈문자열로 생성. 즉, myName = '';
  const [myName, setMyName] = useState('');

  // 이벤트 핸들러로 사용될 함수는 컴포넌트 함수 안에서 정의된다.
  const onMyNameChange = (e) => {
    // e.currentTarget은 jQuery의 $(this)에 해당함.
    // 즉, 이벤트가 발생한 자신 (여기서는 input 태그)
    setMyName(e.currentTarget.value);
  };

  return (
    <div>
      <h2>useState</h2>

      {/* state값을 출력할 때는 단순히 변수값으로서 사용한다. */}
      <h3>
        {myName}님 안녕하세요.
      </h3>
      <hr />

      <div>
        <input
          type="text"
          value={myName}
          onChange={onMyNameChange}
        />
      </div>
    </div>
  );
};

export default MyState;

※ 출력결과

input창에 입력하면 텍스트로 나타난다. (동시적으로)

 

2) useEffect

  • 리액트 컴포넌트가 렌더링 될 때마다 특정 작업을 수행하도록 설정하는 Hook이다.
  • 기본적으로 렌더링 직후마다 실행되며, 두번째 파라미터 배열에 무엇을 넣는지에 따라 싱행되는 조건이 달라진다.
  • 클래스형 컴포넌트의 componentDidMount와 componentDidUpdate를 합친 형태와 비슷하다.
// useEffect import

import React, { useEffect } from 'react';

 

- 렌더링 될 때마다 실행되는 함수 정의

  • 최초 등장하거나 state 값이 변경될 때 모두 실행된다.
useEffect(() => {
  .. 처리할 코드 ..
});

 

- 마운트 될 때만 실행되는 함수 정의

  • 컴포넌트가 마운트될 때 최초 1회만 실행된다. (state 값이 변경 될 때는 실행되지 않는다.)
  • 함수의 두번째 파라미터로 빈 배열을 넣어주면 된다.
useEffect(() => {
  .. 처리할 코드 ..
}, []);

 

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

  • 두번째 파라미터로 전달되는 배열 안에 검사하고 싶은 값을 넣어주면 된다.
useEffect(() => {
  .. 처리할 코드 ..
}, [값이름]);

 

- 언마운트될 때 실행

  • 클로저(리턴되는 함수)를 명시한다.
  • state 값이 변경되어 화면이 다시 렌더링 되거나 화면 이동 등의 이유로 컴포넌트가 사라질 때 실행된다.
  • 오직 언마운트 될 때만 함수를 호출하고 싶다면 두번째 파라미터에 빈배열을 넣어주면 된다.
useEffect(() => {
  return () => {
  	.. 처리할 코드 ..
  }
},[]);

 

3) useRef

  • 함수형 컴포넌트에서 ref를 쉽게 사용할 수 있도록 처리해 준다.
  • 자바스크립트에서 document.getElementById( ) 나 document.querySelector( )로 DOM객체를 가져오는 과정을 React로 표현한 것으로 이해할 수 있다.
import React, { useRef } from 'react';


const MyRef = () => {

  // HTML 태그를 리액트 안에서 참조할 수 있는 변수를 생성
  const myDname = useRef();
  const myLocation = useRef();
  const myResult = useRef();

  return (
    <div>
      <h2>useRef</h2>

      {/* 미리 준비한 컴포넌트 참조변수와 HTML 태그를 연결 */}
      <div>
        <input type="text" ref={myDname} />
      </div>

      <div>
        <input type="text" ref={myLocation} />
      </div>

      <h3>
        입력값: <span ref={myResult} />
      </h3>

      <button
        onClick={(e) => {
          // 컴포넌트 참조변수를 사용해서 다른 HTML 태그에 접근 가능
          // -> "참조변수.current" 해당 HTML을 의미하는 JS DOM 객체
          // -> myDname.current와 document.querySelector or getElementByID 등으로 생성한 객체가 동일한 DOM객체이다.
          
          const dname = myDname.current.value;
          const loc = myLocation.current.value;

          myResult.current.innerHTML = dname + ', ' + loc;
        }}
      >
        클릭
      </button>
    </div>
  );
};

export default MyRef;

※ 출력결과

값을 입력하고 버튼을 클릭하면 해당 값이 출력된다

 

4) useReducer

  • usestate와 비슷하지만, 더 다양한 컴포넌트 상황에 따라 다양한 상태를 다른 값으로 업데이트 하고자 하는 경우 사용된다.
    • usestate의 대체 함수로 이해하면 된다.
  • state값이 다수의 하위값을 포함하거나 이를 활용하는 복잡한 로직을 만드는 경우 useState보다 useReducer를 선호한다.
import React, { useReducer } from 'react';

const [현재상태, reducer함수로전달할값] = useReducer(reducer함수, 초기값);
// usestate와 비슷하지만, 초기값 앞에 상태를 업데이트할 함수를 넣는다

 

- reducer 함수

  • reducer 함수는 현재 상태와 업데이트를 위해 필요한 정보를 담은 action값을 전달 받아 새로운 상태를 반환하는 함수이다.
  • useReducer 에서 전달 받은 값은 action 으로 들어와 state에 따라 action값을 어떻게 할지 정의한다.
function reducer(state, action) {
  return ...;
}

// 함수명은 개발자 마음대로 해도 된다.

 

- useReducer의 구조

import React, { useReducer } from 'react';

function reducer(state, action) {
  return ...;
}

const [현재상태, reducer함수로전달할값] = useReducer(reducer함수, 초기값);
  1. 어떤 이벤트에 의해 reducer함수로전달할값에 값이 들어오면 reducer함수의  action의 파라미터 값으로 이동한다.
  2. reducer 함수 내에서 action 파라미터를 통해 상태를 어떻게 할지 정의하고(대부분 switch문 사용) 그 값을 useReducer의 현재상태 값으로 보낸다.
  3. reducer 함수의 state는 현재상태가 업데이트 되기전의 값을 나타낸다.

 

※ 간단한 예제

import React, { useReducer } from 'react';

function setCounterValue(state, action) {

  // action 값의 상태에 따른 state값을 처리
  switch (action) {
    case 'HELLO':
      return state + 1;  // 현재값 + 1
    case 'WORLD':
      return state - 1;  // 현재값 - 1
    default:        
      return 0;
  }
}

const MyReducer = () => {

  const [myCounter, setMyCounter] = useReducer(setCounterValue, 0);
  //        현재상태, 함수로 보낼 값                     reducer함수, 초기값
  
  return (
    <div>
      <h2>MyReducer</h2>
      <p>현재 카운트 값: {myCounter}</p>
      <button type="button" onClick={(e) => setMyCounter('HELLO')}>
        UP
      </button>
      <button type="button" onClick={(e) => setMyCounter('WORLD')}>
        DOWN
      </button>
      <button type="button" onClick={(e) => setMyCounter('')}>
        RESET
      </button>
    </div>
  );
};

export default MyReducer;

※ 출력결과

초기값에서 'DOWN' 버튼 4번 클릭

 

5) useMemo

  • 함수형 컴포넌트 내에서의 발생하는 연산을 최적화 할 수있다.
  • 숫자, 문자열, 객체처럼 일반 값을 재사용하고자 할 경우 사용한다.
  • useEffect의 특정값이 업데이트될 때만 실행하는 구조와 비슷하다.
    • 두번째 파라미터인 배열에 설정된 state값이 이전 상태와 다를 경우에만 콜백을 실행한다.
const 변수명 = useMemo(() => 사용할함수, [검사할값]);

※ memoized 된 값을 반환한다

  • 컴퓨터 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술

 

※ 간단한 예제

// 출처: https://velog.io/@velopert/react-hooks

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

const getAverage = (numbers) => {
  console.log('평균값 계산중 ..');
  if (numbers.length === 0) return 0;
  const sum = numbers.reduce((a, b) => a + b);
  return sum / numbers.length;
};

const Average = () => {
  const [list, setList] = useState([]);
  const [number, setNumber] = useState('');

  const onChange = (e) => {
    setNumber(e.target.value);
  };

  const onInsert = (e) => {
    const nexList = list.concat(parseInt(number));
    setList(nexList);
    setNumber('');
  };

  return (
    <div>
      <input value={number} onChange={onChange} />
      <button onClick={onInsert}>등록</button>
      <ul>
        {list.map((v, i) => (
          <li key={i}>{v}</li>
        ))}
      </ul>
      <div>
        <b>평균 값: </b>
        {getAverage(list)}
      </div>
    </div>
  );
};

export default Average;
  • 위 코드를 실행해보면 실행은 잘 되지만, input 내용을 수정 할 때마다 getAverage 함수가 호출된다.

'1'을 5번 입력했는데 getAverage함수가 5번 호출된 모습

  • 이 때, 특정값이 바뀌었을 때만 연산을 실행하거나 바뀐 값이 없다면, 이전에 연산했던 결과를 다시 사용할수 있게 해주는게 useMemo이다.
// 출처: https://velog.io/@velopert/react-hooks

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

const getAverage = (numbers) => {
  console.log('평균값 계산중 ..');
  if (numbers.length === 0) return 0;
  const sum = numbers.reduce((a, b) => a + b);
  return sum / numbers.length;
};

const Average = () => {
  const [list, setList] = useState([]);
  const [number, setNumber] = useState('');

  const onChange = (e) => {
    setNumber(e.target.value);
  };

  const onInsert = (e) => {
    const nexList = list.concat(parseInt(number));
    setList(nexList);
    setNumber('');
  };
  
  const avg = useMemo(() => getAverage(list), [list]);

  return (
    <div>
      <input value={number} onChange={onChange} />
      <button onClick={onInsert}>등록</button>
      <ul>
        {list.map((v, i) => (
          <li key={i}>{v}</li>
        ))}
      </ul>
      <div>
        <b>평균 값: </b>
        {avg}
      </div>
    </div>
  );
};

export default Average;
  • avg 변수에 useMemo를 사용하고, getAverage 함수를 선언하고, 검사할 값을 배열에 넣는다.
  • 기존에 호출했던 getAverage함수 대신에 avg 변수 호출한다.

※출력 결과

연산을 수행할 때만 함수가 호출된다.

 

6) useCallback

  • useMemo와 비슷한 함수로 렌더링 성능 최적화에 사용된다
    • memoized 된 콜백을 반환한다.
  • 이벤트 핸들러 함수가 필요한 경우에만 생성할 수 있다.
  • 일반적으로 이벤트 핸들러 함수를 선언하게 되면 컴포넌트가 리렌더링 될 때마다 이벤트 함수들이 새로 생성된다.
    • 대부분의 경우에 이런 방식은 문제가 되지 않지만, 컴포넌트의 렌더링이 자주 발생하거나, 렌더링 해야할 컴포넌트의 개수가 많아진다면 문제가 된다.
import React, { useCallback, useState } from 'react';

const MyCallback = () => {
  const [myText, setMyText] = useState('Hi!');
  
  // 컴포넌트가 최초 렌더링 될 때 1회만 이벤트 핸들러 함수를 정의하고, 이후 부터는 계속적으로 재사용된다.
  // 만약 두 번째 파라미터인 배열에 특정 state값을 지정할 경우 해당 값이 수정될 때만 이벤트가 정의된다.
  const onInputChange = useCallback((e) => {
    setMyText(e.currentTarget.value);
  }, []);

  return (
    <div>
      <h2>useCallback</h2>
      <h3>{myText}</h3>
      <input
        type="text"
        placeholder="input..."
        onChange={onInputChange}
      ></input>
    </div>
  );
};

export default MyCallback;

※ 출력결과

'국비수업 > React' 카테고리의 다른 글

[React] Axios-hooks  (0) 2022.05.17
[React] Axios 사용하기  (0) 2022.05.15
[React] CSS 사용하기  (0) 2022.04.26
[React] Props (Properties)  (0) 2022.04.25
[React] JSX (JavaScript eXtension)  (0) 2022.04.23

+ Recent posts