[React]내가 알아보기 쉽게 정리한 React-hooks

김진평·2023년 2월 9일
0

React

목록 보기
2/3
post-thumbnail

useState

state란?

react에서 state는 간단하게 생각하면 그냥 변수이다.
그러나 일반 변수와 차이점이 있는데, 그것은 바로 값이 변하면 브라우저 리랜더링이 일어난다는 것이다.

사용법

import {useState} from 'react';

export function App() => {
    const [변수명, 함수명] = useState(초기 값);
  
    return ();
}
import "./App.css";
import { useState } from "react";

console.log(number);

function App() {
  const [number, setNumber] = useState(0);
  
  return (
    <div>
      <div className="div">
        <input
          type="number"
          value={number}
          onChange={(e) => setNumber(e.target.value)}
        />
      </div>
    </div>
  );
}

export default App;

예시에서 number라는 변수는 비구조화 할당으로 초기 값 0으로 선언되었으며
input 태그가 변할 때마다 setNumber 함수를 통해 number의 상태를 update 시킨다.

작동 원리에 대해서 깊게 공부가 끝나면 추가로 이어서 작성할 예정이다.
대략적으로 클로저 함수를 통해 useState가 선언될 당시의 초기 값을 setState 함수를 호출할 때 이용한다는 것을 알게 되었다.


useEffect

useEffect를 알아보기에 앞서 side Effect란?

→ 내가 의도하지 않은 다른 동작을 한다.
→ console.log는 대표적인 side effect이다.
→ 즉, 외부에 있는 어떤 값을 읽어오거나 사용하는 경우를 side effect라고 한다.
→ Data Fetching, DOM 접근 및 조작, 구독(Subscribe) 등이 side effect이다.
//ex) side effect가 없는 경우
const sum = (num) => {
  return num + 1;
}
//sum 함수 호출시 함수 외부의 요소에 접근하지 않는다.
//ex) side effect가 있는 경우
const newNum = 1;
const sum = () => {
    return nuewNum + 1;
}
//sum 함수 호출시 함수 외부의 newNum 값을 변경시켰기 때문

side Effect를 잘 관리해주지 않으면 렌더링 과정에서 여러가지 문제가 생길 수 있으며, 이 때문에 useEffect를 사용한다.

사용법

import {useEffect} from 'react'

export function App() => {
  useEffect(() => {
//내용 작성
  }, [])
  return ();
}
useEffect의 첫 번째 인자는 함수, 두 번째 인자는 의존성 배열을 받는다.
첫 번째로 전달받는 함수 인자는 렌더링 이후 실행된다.
두 번째로 전달받는 배열 인자는 비어있을 경우 
렌더링 될 때 한 번만 첫 번째 인자 함수를 수행하며, 
배열 인자가 존재할 경우 해당 인자가 변경될 때마다 첫 번째 인자인 함수를 수행한다.

  #### case1
```javascript
import { useEffect, useState } from "react";

function App() {
  const [cnt, setCnt] = useState(1);

  useEffect(() => {
    console.log("enter useEffect!!");
  }, []);

  return (
    <div>
      {cnt}
      <button onClick={() => setCnt((prev) => prev + 1)}>+</button>
    </div>
  );
}

export default App;
//result => 첫 마운트시에만 "enter useEffect" 출력

결과

case2

import { useEffect, useState } from "react";

function App() {
  const [cnt, setCnt] = useState(1);

  useEffect(() => {
    console.log("enter useEffect!!");
  }, [cnt]);

  return (
    <div>
      {cnt}
      <button onClick={() => setCnt((prev) => prev + 1)}></button>
    </div>
  );
}

export default App;
//result => 첫 마운트시 + 버튼 클릭시마다 "enter useEffect" 출력

결과


useMemo

useMemo란 리렌더링시 동일한 값을 반환하는 함수를 반복적으로 호출하지 않도록 값을 메모이제이션(메모리에 할당)하여 해당 값이 변경될 때만 함수를 호출하도록 해주는 hook이다.

사용법

const temp = useMemo(() => { }, [item]);

useMemo는 첫 번째 인자로 콜 백 함수, 두 번째 인자로 의존성 배열을 입력받는다.
의존성 배열의 값이 업데이트 될 때가 아니라면 렌더링되지 않는다.
의존성 배열을 빈 값으로 입력하면 최초 mount시에만 동작한다.

import { useMemo, useState } from "react";

function App() {
  const [add, setAdd] = useState(0);
  const [subtract, setSubtract] = useState(0);
  //-----------------------useMemo사용----------------
  const addResult = useMemo(() => {
    return addFunc(add);
  }, [add]);
  const subtractResult = useMemo(() => {
    return subtractFunc(subtract);
  }, [subtract]);
  //-------------------------------------------------

  function addFunc(val) {
    console.log("add");
    return val + 10;
  }

  function subtractFunc(val) {
    console.log("subtract");
    return val - 10;
  }
  return (
    <div>
      <div className="div">
        <input
          type="number"
          value={add}
          onChange={(e) => setAdd(parseInt(e.target.value))}
        />
        &nbsp; + 10 = {addResult}
      </div>
      <div className="div">
        <input
          type="number"
          value={subtract}
          onChange={(e) => setSubtract(parseInt(e.target.value))}
        />
        &nbsp; - 10 = {subtractResult}
      </div>
    </div>
  );
}

export default App;

위 코드는 input 태그에 입력된 숫자 ± 10을 해주는 코드이다.
입력된 숫자 ±를 실행하는 addFunc, subtractFunc 함수에 console.log를 통해 렌더링을 확인할 수 있다.
해당 두 함수는 useMemo를 사용했으므로 useState 변수인 "add", "substract" 변수가 변경하더라도
값이 변하지 않는다면 렌더링을 진행하지 않는다.

결과


useCallback

useCallback이란 useMemo와 유사한 기능으로 불필요하게 렌더링 되는 것을 방지하기 위한 hook이다.

useMemo는 의존성배열에 입력된 이 update될 때 렌더링을 진행하도록 하지만

useCallback는 의존성배열에 입력된 함수가 update될 때 렌더링을 진행하도록 한다.

사용법

const anyFunc = () => {};
const temp = useCallback(() => { }, [anyFunc]);

useCallback은 첫 번째 인자로 함수, 두 번째 인자로 의존성 배열을 입력받는다.
의존성 배열의 함수가 업데이트 될 때가 아니라면 렌더링되지 않는다.
의존성 배열을 빈 값으로 입력하면 최초 mount시에만 동작한다.

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

function App() {
  const [number, setNumber] = useState(0);

  const getNumber = () => {
    console.log(`입력된 숫자는 ${number} 입니다.`);
    return;
  };

  useEffect(() => {
    console.log("getNumber함수가 변경되었습니다.");
  }, [getNumber]);

  return (
    <div>
      <div className="div">
        <input
          type="number"
          value={number}
          onChange={(e) => setNumber(e.target.value)}
        />
        <button onClick={getNumber}>Call function</button>
      </div>
    </div>
  );
}

export default App;
	```

위 코드를 살펴보자.
먼저 최초로 "getNumber함수가 변경되었습니다."가 콘솔로 찍힌다.
그리고 Call Function 버튼을 누르면 "입력된 숫자는 0입니다." 가 출력된다.
숫자를 up / down 시키면 위 문구가 계속해서 찍히는 것을 알 수 있다.
그리고 다시 Call Function 버튼을 누르면 현재 입력된 숫자가 반영되어
"입력된 숫자는 ${현재 숫자}입니다."가 출력된다.

  #### 결과
![](https://velog.velcdn.com/images/jin_jin_dev/post/f936da73-d3a1-4c91-ad0d-a4dbd9db11de/image.gif)

여기서는 사실 비효율적인 로직을 수행하고 있다.

getNumber는 함수이기 때문에 변수에 저장할 때 별도의 메모리와 주소를 부여받는다.
즉, 숫자를 up / down 시킬 때마다 각각 메모리 공간을 차지하는 것이다.

이런 불필요한 메모리 할당을 하지 않기 위해 useCallback을 사용한다.
이제 getNumber 함수를 아래와 같이 바꿔보자
  ```js
const getNumber = useCallback(() => {
  console.log(`입력된 숫자는 ${number} 입니다.`);
  return;
}, []);
결과

getNumber 함수를 useCallback으로 감싸주었다.
이후 값을 up / down 시켜도 "getNumber 함수가 변경되었습니다." 콘솔이 출력되지 않는다.
이는 useCallback에서 첫 번째로 입력된 함수에 변경사항이 없기 때문이다.

여기서 한 가지 의문이 든다.
number가 계속 변하는데 왜 함수에 변화가 없지?

답은 이렇다.

useCallback을 선언하는 시점에 number는 0이며 해당 함수가 메모이제이션 된다.
우리가 number를 변경하더라도 메모이제이션 된 getNumber를 (즉, number가 0일 때의) 사용한다.

자, 다시 아래와 같이 수정해보자.

const getNumber = useCallback(() => {
  console.log(`입력된 숫자는 ${number} 입니다.`);
  return;
}, [number]);
결과

의존성 배열에 number를 입력해두면 number가 바뀔 때마다 함수에도 변경이 생기기 때문에 다시 메모이제이션된다.

useCallback을 사용할 때는 이 부분을 조심해서 사용하자!!


0개의 댓글