천진난만 코딩 스토리

2023.03.03) 항해 26일차 (2-React Hooks) 본문

TIL(Today I Learned)

2023.03.03) 항해 26일차 (2-React Hooks)

Wisdom_1104 2023. 3. 3. 22:54

리액트 훅.......

이게 매우 중요하다는 것도 알고, 어려운 내용이고 공부할 양이 많다고 다들 말해주셨다.

근데 주어진 자료를 보고 자료수집을 더 해보아도,

어떻게 사용하는 것인지, 어떤 상황에서 사용하는지 감은 잡히지만,

막상 코드로 작성해보려니 코드적인 상황이 떠오르지 않아서 좀 멘탈이 터졌다...

많이 사용해보고 적응해야 하는 건 알지만, 아직은 많이 사용해볼 만한 상황을 못 찾았다........

 

1) Reat Hooks

① useState

setState를 사용하는 방식에는 우리가 알고 있는 방식이 아닌 또 다른 방식인 함수형 업데이트 방식이 있다.

일반 업데이트 방식은 버튼을 클릭했을 때 setNumber가 각각 실행되는 것이 아니라, 배치(batch)로 처리한다.

-아래코드를 참고하여 설명하자면-

우리가 onClick을 했을 때 setNumber 라는 명령을 세번 내리지만, 리액트는 그 명령을 하나로 모아 한번만 실행한다.

그래서 setNumber을 3번 명령하던, 100번 명령하던 1번만 실행된다.

반면에 함수형 업데이트 방식은 3번을 동시에 명령을 내리면, 그 명령을 모아 순차적으로 각각 1번씩 실행한다.

0에 1더하고, 그 다음 1에 1을 더하고, 2에 1을 더해서 3이 되는 것이다.

useState가 이렇게 동작하는 이유는,

리액트는 불필요한 리-렌더링을 방지(렌더링 최적화)하기 위해

즉, 리액트의 성능을 위해 한꺼번에 state를 업데이트 하기에 나타나는 결과이다.

const App = () => {
  const [number, setNumber] = useState(0);
  return (
    <div>
			// 일반적인 방법 {/* 버튼을 누르면 1씩 플러스된다. */}
      <div>{number}</div> 
      <button
        onClick={() => {
          setNumber(number + 1); // 첫번째 줄 
          setNumber(number + 1); // 두번쨰 줄
          setNumber(number + 1); // 세번째 줄
        }}
      >
        버튼
      </button>
			//함수형 업데이트 방법 {/* 버튼을 누르면 3씩 플러스 된다. */}
      <div>{number}</div>
      <button
        onClick={() => {
          setNumber((previousState) => previousState + 1);
          setNumber((previousState) => previousState + 1);
          setNumber((previousState) => previousState + 1);
        }}
      >
        버튼
      </button>
    </div>
  );
}

export default App;
```

 

② useEffect

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

어떤 컴포넌트가 화면에 보여졌을 때(혹은 사라졌을 때) 내가 무언가를 실행하고 싶다면 useEffect를 사용한다.

//useState와 마찬가지로 React에서 제공하는 훅 (기능) 이므로,
import React, { useEffect } from "react"; 
//로 import 해서 사용해야한다.

useEffect는 useEffect가 속한 컴포넌트가 화면에 렌더링 될 때 실행된다.

그렇기에 원치 않는 상황에서도 렌더링에 의해 실행되는 경우도 있다.

이 경우에는 의존성 배열을 사용하여 해결할 수 있다.

❶ 의존성 배열(dependency array)

"이 배열에 값을 넣으면 그 값이 바뀔 때만 useEffect를 실행할게" 라는 것 이다.

// useEffect의 두번째 인자가 의존성 배열이 들어가는 곳 입니다.
useEffect(()=>{
	// 실행하고 싶은 함수
}, [의존성배열])

어떤 함수를 컴포넌트가 렌더링 될 때 단 한번만 실행하고 싶으면 의존성 배열을 [ ] 빈 상태로 넣으면 된다.

 

 

➂ useRef

DOM 요소에 접근할 수 있도록 하는 React Hook 이다.

리액트에서도 DOM을 선택해야 할 상황이 생길 때, useRef hook을 사용한다.

ex) 화면이 렌더링 되자마자 특정 input 태그가 focusing이 돼야 하는 경우 등

 

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

ref.current = "바꾼 값";
console.log("ref 1", ref);

다만, 이렇게 설정된 ref 값은 컴포넌트가 계속해서 렌더링 되어도 unmount 전까지 값을 유지한다는 것을 주의해야한다.

이러한 특징 때문에 useRef는 다음 2가지 용도로 사용된다.

  • 저장공간
    • state와 비슷한 역할을 한다. (다만 state는 변화가 일어나면 다시 렌더링이 일어나서 내부 변수들은 초기화)
    • ref에 저장한 값은 렌더링을 일으키지 않는다. (ref의 값 변화가 일어나도 내부 변수들이 초기화 X)
    • state리렌더링이 꼭 필요한 값을 다룰 때 쓰면 된다.
    • ref리렌더링을 발생시키지 않는 값을 저장할 때 사용한다.
  • DOM
    • 렌더링 되자마자 특정 input이 focusing 돼야 한다면 useRef를 사용한다.

 

useContext

props를 하여 GrandFather 컴포넌트 > Father 컴포넌트 > Child 컴포넌트로 name을 전달해준다하면,

Father 컴포넌트는 중간다리 역할을 하게 된다. 근데 만약,  Father 컴포넌트가 100개가 넘는다면?

굉장히 비효율적이다. 이 비효율적인 방법을 대신하도록 useContext를 사용할 수 있다.

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

// 여기서 null은 createContext의 기본값이다.
export const FamilyContext = createContext(null);

context 를 생성하고,

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

function GrandFather() {
  const name="olaf";

  return (
    <FamilyContext.Provider value={{ name }}> //Provider란 제공자
      <Father /> //context에서 전해주기에 props가 필요없다.
    </FamilyContext.Provider>
  );
}

export default GrandFather;

context에서 전해줄 것이기 때문에 더 이상 props가 필요없다.

그렇기에  FamilyContext.Provider로 전해주고,

props를 받던 Father 컴포넌트에서도 Child에게 props를 줄 필요가 없다.

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

function Child({ name }) {
  const data = useContext(FamilyContext); //적용

  return (
    <div>
      이름은 <span style={stressedWord}>{data.name}</span>랍니다.
      //props로 내려준 값이 아니라 context를 이용해서 값을 받아옴.
    </div>
  );
}

export default Child;
  • 주의할 점
  • useContext를 사용할 때, Provider에서 제공한 value가 달라진다면,
  • useContext를 사용하고 있는 모든 컴포넌트가 리렌더링 된다.
  • value 부분을 항상 신경써야 하고, 그 대안으로 메모이제이션이 있다.