TIL 231109 React hooks

두두맨·2023년 11월 9일
0

앞서 React에서 사용하는 hook 중 하나인 useState 에 대해 알아봤었다.

오늘은 그 외에 다른 hook들에 대해 알아보려 한다.

목차

  1. useEffect
    • 사용 목적
    • 리렌더링 시 문제점
  2. useRef
    • 사용 방법
    • 사용 목적

1. useEffect 란?


렌더링 될 때 , 특정한 작업을 수행해야 할 때 설정하는 훅을 말한다. 대표적으로 필요한 이유는 다음과 같다.

  • 사용목적

useEffect는 리액트 컴포넌트가 렌더링될 때마다 특정 작업을 수행하도록 설정할 수 있는 Hook이다. 1)어떤 컴포넌트가 화면에 보여졌을 때 내가 무언가를 실행하고 싶거나 2)어떤 컴포넌트가 화면에서 사라졌을 때 무언가를 실행하고 싶을 때 useEffect를 사용한다.


화면에 보여졌을 때 어떤 걸 실행하고 싶다면,

import React, { useEffect } from "react";

const App = () => {

  useEffect(() => {
		// 이 부분이 실행된다.
    console.log("hello useEffect");
  });

  return <div>Home</div>;
}

export default App;

{useEffect} 를 리액트로 import 한 후,
return 할 값 위에 useEffect 함수를 만들어 실행하고자 하는 로직을 넣어주면 된다. 그러면 브라우저가 실행될 때, useEffect 안의 console.log가 실행된다.

  • 리렌더링 시 문제점

1) input에 값을 입력
2) value, 즉 state가 변경
3) state가 바뀌었기 때문에 App컴포넌트 리렌더링
4) 리렌더링 -> useEffect()
5) 1~4번 계속 반복 (인풋창에 값이 변경될때마다 렌더링 새로 하고 있다는거임!) 

이를 해결하기 위해 의존성 배열(dependency array)을 사용할 수 있다.

그게 뭘까?
이 배열에 값을 넣으면, 그 값이 바뀔 때만 useEffect를 실행한다! 라는 뜻이다.

그저 [] 이 배열만 하나 추가하는 것이다. 물론 이 안에 값을 넣어줘도 되기는 하는데, 공백으로 놔두면 어떤 값이 들어와도 바뀌었다고 볼 수 없으니까 처음에 렌더링이 될 때만 useEffect를 실행하고, 그 다음엔 실행하지 않는다. 즉, 리렌더링이 일어나지 않는다는 말이다!


그럼 의존성 배열 자리에 값을 넣어주면 console.log는 어떻게 실행될까 ?

useEffect(() => {
console.log(`hello useEffect! : ${value}`);
}, [value]);

이렇게 적어주면 인풋창 값 변경될 때마다, 렌더링 될 때 마다 변경되는 값을 확인할 수 있다.

2. useRef란?

DOM 요소에 접근할 수 있게 해주는 hook을 말한다.


  • 사용방법

이런 식으로 사용 가능하다.

위 코드 대로 콘솔을 띄우면, 이런 식으로 값 변경도 가능하다.

이렇게 설정된 ref 값은 컴포넌트가 계속해서 렌더링이 되어도, 컴포넌트가 unmount, 즉 죽기 전까지는 값을 유지한다 !!!

  • 사용목적


1. 저장공간

  • state는 변화가 일어나면 다시 렌더링 되고, 함수형 컴포넌트가 다시 그려진다. 결정적으로 내부 변수는 초기화된다.
  • 하지만 ref에 들어간 값은 렌더링을 일으키지 않는다. 내부 변수 초기화 되는 것 막을 수 있다.
  • 컴포넌트가 100번 렌더링 되도 ref 안에 들어간 값은 유지됨.
  • state는 렌더링이 꼭 필요한 값 다룰 때 쓰고, ref는 리렌더링 발생시키지 않는 값 저장 시 사용!

코드와 화면을 통해 결과를 보자!

import React, { useRef, useState } from 'react';
const style = {
  border: '1px solid black',
  margin: '10px',
  padding: '10px',
};
function App() {
  const [count, setCount] = useState(0);
  const countRef = useRef(0);

  const plusStatecountButtonHandler = () => {
    setCount(count + 1);
  };

  const plusRefcountButtonHandler = () => {
    countRef.current++;
    console.log(countRef.current);
  };

  return (
    <>
      <div style={style}>
        state 영역입니다.{count} <br />
        <button onClick={plusStatecountButtonHandler}>state 증가</button>
      </div>
      <div style={style}>
        ref 영역입니다. {countRef.current}
        <br />
        <button onClick={plusRefcountButtonHandler}>ref 증가</button>
      </div>
    </>
  );
}

export default App;

state 문법과 ref 문법을 사용해 작성한 코드로 각자의 공간과 버튼을 만들었다.
둘다 버튼을 누르면 박스 안에 적힌 숫자가 1씩 증가하도록 하는 로직이 작성되었다.

하지만 ?

ref 영역은 아무리 버튼을 눌러도 콘솔창에서만 숫자가 증가되고, 브라우저에서는 반영이 되지 않는 모습을 볼 수 있다!

정리하면,

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




2. 원하는 DOM 접근 위해

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

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

  // 화면이 렌더링 될 때, 어떤 작업을 하고 싶다 ! : useEffect !
  useEffect(() => {
    idRef.current.focus();
  }, []);
  return (
    <>
      <div>
        아이디 : <input type='text' ref={idRef} />
      </div>
      <div>
        비밀번호 : <input type='password' />
      </div>
    </>
  );
}

export default App;

위 코드를 구현하면 다음과 같이 브라우저 첫 렌더링 시, 아이디 인풋창에 포커싱이 된 채로 나타나는 것을 볼 수 있다.


그럼 아이디 창에 텍스트 10자 이상 적히면 자동으로 비밀번호 창에 포커싱되어 리렌더링이 되도록 할 수도 있을까?
import React, { useEffect, useRef, useState } from 'react';

function App() {
  const idRef = useRef('');
  const pwRef = useRef('');

  const [id, setId] = useState('');
  // 화면이 렌더링 될 때, 어떤 작업을 하고 싶다 ! : useEffect !
  useEffect(() => {
    idRef.current.focus();
  }, []);

  useEffect(() => {
    if (id.length >= 10) {
      pwRef.current.focus();
    }
  }, [id]);
  return (
    <>
      <div>
        아이디 :{' '}
        <input
          value={id}
          onChange={(event) => {
            setId(event.target.value);
          }}
          type='text'
          ref={idRef}
        />
      </div>
      <div>
        비밀번호 : <input type='password' ref={pwRef} />
      </div>
    </>
  );
}

export default App;

초기 아이디 값 부여하고, 비밀번호에도 const pwRef = useRef(''); 이렇게 Ref 로 만들어준다.
그리고 추가 useEffect를 만들어 그 안에 if문으로 조건 만들어주고, Onchange 속성에 변경될 id 걸어주면 끝!


근데, 굳이 useEffect 하나 더 만들어서 비밀번호 창으로 이동시키는거 말고, id Onchange 속성에다가 자리수가 10자리이면 비밀번호창으로 옮겨지는 코드 적어주면 안되나? 라고 말할 수 있다.

그럼 바로 실행 해보면 되지 !

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

function App() {
  const idRef = useRef('');
  const pwRef = useRef('');

  const [id, setId] = useState('');
  // 화면이 렌더링 될 때, 어떤 작업을 하고 싶다 ! : useEffect !
  useEffect(() => {
    idRef.current.focus();
  }, []);

  // useEffect(() => {
  //   if (id.length >= 10) {
  //     pwRef.current.focus();
  //   }
  // }, [id]);
  return (
    <>
      <div>
        아이디 :{' '}
        <input
          value={id}
          onChange={(event) => {
            setId(event.target.value);
            console.log('id', id);
            if (id.length >= 10) {
              pwRef.current.focus();
            }
          }}
          type='text'
          ref={idRef}
        />
      </div>
      <div>
        비밀번호 : <input type='password' ref={pwRef} />
      </div>
    </>
  );
}

export default App;

위의 useEffect는 잠시 주석처리하고, onChange에 로직을 넣어봤다.
이제 실행해보자.

분명히 10자리 치면 비밀번호 창으로 넘어가기로 했는데,
11자리를 쳐야 넘어간다.

콘솔창에 뜨는 것도 input창에 친 값보다 하나씩 늦게 반환된다.

setId를 했다고 바로 적용이 되는 것이 아니다.
리액트는 batch update로 처리하기 때문이다!

그게 뭐냐면, 리액트 성능을 위해 setState를 단일 업데이트 (batch update)로 한번에 처리한다는 거다. 즉, 모아서 한 번에 처리하기 때문에 늦게 반환될 수도 있다는 것!

이렇게 벌써 useState, useEffect, useRef, 세 개의 React hook을 알게 되었다.
부지런히 코드 쳐봐야 좀 익숙해질 수 있을 것 같다.

profile
병아리 개발준비생 🐥

0개의 댓글