TIL #20 | React에서 사용하는 Hooks 알아보기

kibi·2023년 11월 7일
1

TIL (Today I Learned)

목록 보기
19/83

useState

리렌더링을 위해 컴포넌트 내부 값의 변경을 도와주는 React Hook

함수형 업데이트

// 기존 업데이트 방법
setState(number + 1);

// 함수형 업데이트 방법
setState(() => {});
  1. 기존 업데이트 방법

    • 배치 업데이트
      - 같은 명령이 여러 개 있다면 최종적으로 한번만 실행한다.
      - 리액트의 성능을 위해 한꺼번에 setState를 단일업데이트로 한번에 처리한다.
  2. 함수형 업데이트

    • 순차적으로 모든 명령을 한번씩 실행한다.

useEffect

컴포넌트가 렌더링 될 때마다 특정한 작업을 수행하도록 설정할 때 사용하는 Hook

  import React, { useEffect } from "react"
  
  useEffect(() => {
	  // 실행할 함수
    console.log("hello");
  }, [ ]); // 의존성 배열

의존성 배열 [ ]

  • 의존성 배열 값이 변경될 때 렌더링 된다.
  • 값이 없이 빈배열 [ ] 일 경우, 처음 렌더링 될 때 딱 한번만 실행된다.

clean up

  • 컴포넌트가 화면에서 사라졌을 때 함수를 실행한다.
import React, { useEffect } from "react";

const App = () => {
	useEffect(() => {
		// 화면에 컴포넌트가 나타났을 때
		// mount될 때 실행하고자 하는 함수
		return () => {
			// 화면에서 컴포넌트가 사라졌을 때
			// umount될 때 실행하고자 하는 함수
		}
	}, [])

	return <div>Hello world!</div>
}

useRef

DOM 요소에 접근할 수 있도록 하는 React Hook
특정 DOM 요소를 선택하기 위해 사용한다.

import React, { useRef } from "react";

  const ref = useRef("초기값");
  console.log("ref", ref);

  ref.current = "변경값"; // 변경
  console.log("ref", ref);

  • 저장공간으로써 사용
    - ref에 저장한 값은 렌더링을 일으키지 않기 때문에 ref 값에 변화가 일어나도 내부 변수들이 초기화 되는 것을 막을 수 있다.
    - 리렌더링 -> 함수 재호출 -> 내부변수 초기화
    - 컴포넌트가 계속 렌더링 되어도 unmount 전까지 값을 유지한다.

useState vs useRef

function App() {
  // state 초기 세팅
  const [count, setCount] = useState(0); 
  // ref 초기 세팅
  const countRef = useRef(0);

  // useState 사용
  const plusStateCountButtonHandler = () => {
    setCount(count + 1);
  };
  // useRef 사용
  const plusRefCountButtonHandler = () => {
    countRef.current++;
    console.log(countRef.current);
  };

  const styled = { border: "1px solid black", margin: "10px", padding: "10px" };
  return (
    <>
      <div style={styled}>
        state 영역입니다. {count} <br />
        <button onClick={plusStateCountButtonHandler}>state 증가</button>
      </div>
      <div style={styled}>
        ref 영역입니다. {countRef.current} <br />
        <button onClick={plusRefCountButtonHandler}>ref 증가</button>
      </div>
    </>
  );
}

useState를 사용할 경우 setState가 실행될 때 리렌더링이 발생하기 떄문에 바로 화면에 반영 되지만 useRef를 사용할 경우 렌더링이 일어나지 않기 때문에 화면에 바뀐 값이 반영되지 않는다.
useRef의 경우 console.log로 값을 확인해봤을 때는 값이 변경되지만 화면에는 반영되지 않는 것을 알 수 있다.

state는 리렌더링이 꼭 필요할 값을 다룰 때 사용하고,
ref는 리렌더링을 발생시키지 않는 값을 저장할 때 사용한다.

ref를 사용하여 포커싱하는 방법

1. input의 ref로 포커싱 설정하는 방법

  • input 태그에는 ref라는 속성이 있어서 이를 통해 해당 DOM 요소에 접근할 수 있다.
import React, { useEffect, useRef } from "react";

function App() {
  const idRef = useRef("");

  useEffect(() => {
    idRef.current.focus(); // input에 설정된포커싱
  }, []);

  return (
    <>
      <div>
        아이디 : <input type="text" ref={idRef} />
      </div>
      <div>
        비밀번호 : <input type="password" />
      </div>
    </>
  );
}

export default App;

2. useRef 사용하여 포커싱 설정하는 방법

  • 아이디 값이 10자리를 넘을 때 패스워드 포커싱하기
import React, { useEffect, useRef, useState } from "react";

function App() {
  const [id, setId] = useState("");

  const idRef = useRef("");
  const pwRef = useRef("");

  useEffect(() => {
    idRef.current.focus();
  }, []);

  // id값이 바뀔 때마다 실행
  useEffect(() => {
    if (id.length >= 10) {  // id값이 10자리를 넘을 경우
      pwRef.current.focus(); // pw에 포커싱
    }
  }, [id]);

  const handleId = (e) => {
    setId(e.target.value);
  };

  return (
    <>
      <div>
        아이디 :{" "}
        <input type="text" ref={idRef} value={id} onChange={handleId} />
      </div>
      <div>
        비밀번호 : <input type="password" ref={pwRef} />
      </div>
    </>
  );
}

export default App;


useContext

prop drilling의 문제점

  • prop이 어떤 컴포넌트로부터 왔는지 파악이 어렵다.
  • 어떤 컴포넌트에서 오류가 발생할 경우 추적이 힘들어져서 대처가 늦을 수 있다.

이러한 문제점을 해결하기 위해 react context API를 사용할 수 있다.
useContext hook를 통해 우리는 전역 데이터를 관리할 수 있다

context API

  • createContext: context 생성
  • Provider: context 전달(to 하위 컴포넌트)
  • Consumer: context 변화 감지

1) createContext import

// context/FamilyContext.js
import { createContext } from "react";

export const FamilyContext = createContext(null);

2) 값을 내려줄 상위 컴포넌트에서 provider 지정

import React from "react";
import { FamilyContext } from "../context/FamilyContext";
import Father from "./Father";

function GrandFather() {
  const houseName = "Kim";
  const poketMoney = 10000;

  return (
    <FamilyContext.Provider // provider로 값을 내려준다.
      value={{
        houseName,
        poketMoney,
      }}
    >
      <Father />
    </FamilyContext.Provider>
  );
}

export default GrandFather;

3) 값을 받을 컴포넌트에서 useContext로 값 가져오기

import React, { useContext } from "react";
import { FamilyContext } from "../context/FamilyContext";

function Child() {
  const data = useContext(FamilyContext); // 값 가져오기
  console.log("data", data);

  return (
    <div>
      <p>집 이름 : {data.houseName}</p>
      <p>용돈 : {data.poketMoney}</p>
    </div>
  );
}

export default Child;

요약 및 회고

(1) useState를 다시 복습하면서 useState로 기본적인 배치 업데이트를 할 경우 같은 명령을 실행할 때 한번만 실행되며, 순차적으로 모든 명령을 실행하기 위해선 setState(() => {})와 같이 함수형 업데이트를 사용한다는 것을 알게 되었다.

(2) useEffect는 컴포넌트가 렌더링 될 때마다 특정한 작업을 수행할 수 있도록 도와주는 Hook인데 첫번째 인자에 실행할 함수를 작성하고 두번째 인자엔 의존성 배열을 넣는다. 의존성 배열이 빈값일 경우 처음 렌더링 될 때 딱 한번만 실행되고, 값을 지정해주었을 땐 값이 변경될 때마다 렌더링 된다.
이런 특징으로 불필요한 리렌더링을 하지 않고 싶을 때나 특정 값이 변경될 때마다 함수를 실행하고 싶을 때 useEffect를 유용하게 사용할 수 있을 것 같다.
useEffect에서는 마운트될 때 실행하고자 함수를 지정할 수 있을 뿐만 아니라, 언마운트될 때 실행하고자 하는 함수를 return () => {} 안에 작성해서 지정할 수 있는데 이것을 clean up이라고 한다. 화면에 나타날 때와 사라질 때 함수를 각각 지정하기 위해 사용할 수 있다.

(3) useRef는 DOM 요소에 접근할 수 있도록 하는 Hook로써 vanila JS에서 돔을 제어하는 방식과 비슷하게 특정 DOM 요소를 선택하기 위해 사용된다. 기본적으로 저장공간으로써 사용하는데 이때 ref에 저장한 값은 state를 사용할 때와 다르게 렌더링을 일으키지 않기 때문에 ref 값에 변화가 일어나도 렌더링이 되지 않는다.
즉, ref 값에 변화가 일어나도 내부 변수들이 초기화 되는 것을 막기 때문에 컴포넌트가 계속 렌더링 되어도 언마운트 전까지 값을 유지할 수 있도록 한다. 이러한 특성 때문에 리렌더링이 꼭 필요한 값을 다룰 땐(화면에 반영할 값) state를 사용하고, 리렌더링을 발생시키지 않는 값을 저장할 땐 ref를 사용한다.

(4) useContext는 prop drilling의 문제점(prop의 출처 확인 어려움, 오류 발생시 추적의 어려움)을 해결하기 위한 hook이다. useContext를 통해 context API인 createContext, Provider, consumer을 사용하여 전역 데이터를 좀 더 편리하게 관리할 수 있다.

0개의 댓글