본문 바로가기
내일배움캠프/Today I Learned

[TIL 2023.06.19] React Styled components / useEffect / useRef

by 괴코딩 2023. 6. 20.

💻오늘 배운 내용

Styled components

CSS-in-JS 방식으로 컴포넌트를 꾸밀 수 있게 도와주는 패키지

vscode-styled-components extension 설치

설치 후 터미널에서 명령어 실행

yarn add styled-components

컴포넌트에 import 해서 사용한다

import styled from "styled-components";

 

사용예시)

const StBox = styled.div`
  width: 100px;
  height: 100px;
  border: 1px solid red;
  margin: 20px;
`;

const App = () => {
  return <StBox>박스</StBox>;
};

export default App;

이 또한 자바스크립트이기 때문에 스타일 코드를 JS코드 작성하듯이 스타일 코드를 작성할 수 있다.

 

props를 통해서 정보를 전달해줄 수 있다.

예)

const StBox = styled.div`
  border: 1px solid ${(props) => props.borderColor}; 
  // 부모 컴포넌트에서 보낸 props를 받아 사용. 
`;

const App = () => {
  return (
    <div>
	{/* props를 통해 borderColor라는 값을 전달. */}
      <StBox borderColor="red">빨간 박스</StBox>
    </div>
  );
};

export default App;

또는 map과 switch문을 이용해서 리팩토링을 할 수 있다. 

등등 여러가지 응용이 가능하다.

 

useEffect

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

화면에 보여졌을 때 or 컴포넌트가 화면에서 사라졌을 때 무언가를 실행하고 싶다면 useEffect를 사용

useState와 마찬가지로 React에서 제공하는 hook이므로 import 해서 사용한다.

 

의존성 배열(dependency array)

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

이런 useEffect의 특징에 의해 의도치 않은 동작을 경험할 수도 있는데, 이때 필요한 것이 의존성 배열이다.

 

의존성 배열이 빈 배열인 경우

useEffect(() => {}, []);

렌더링 될 때 단 한번만 실행하고 싶으면 의존성 배열을 [ ] 빈 상태로 넣으면 된다

 

의존성 배열에 값이 있는 경우

useEffect(() => {}, [value]);

이 배열에 값을 넣으면 그 값이 바뀔 때만 useEffect를 실행한다는 의미

 

clean up

 useEffect(() => {
    return () => {
      console.log(
        "안녕히 계세요 여러분! 전 이 세상의 모든 굴레와 속박을 벗어 던지고 제 행복을 찾아 떠납니다! 
        여러분도 행복하세요~~!"
      );
    };
  }, []);

useEffect 안에서 return을 해주고 이 부분에 실행되길 원하는 함수를 넣으면 된다.

 

useRef

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

바닐라 자바스크립트로 생각하면 getElementById, querySelector 등과 맥을 같이 한다 생각하면 이해하기 편하다.

*ref 값은 컴포넌트가 계속해서 렌더링 되어도 unmount 전까지 값을 유지

 

이러한 특징으로 2가지 용도로 사용된다.

 

1. 저장공간

state와 차이점이라 할 수 있는데, state는 변화가 일어나면 다시 렌더링된다. 내부 변수들은 초기화 되는 것.

그러나 ref에 저장한 값은 렌더링을 일으키지 않는다.

즉, ref의 값 변화가 일어나도 렌더링으로 인해 내부 변수들이 초기화되는 것을 막을 수 있다.

 

따라서!

state -> 리렌더링이 꼭 필요한 값을 다룰 때 사용

ref -> 리렌더링을 발생시키지 않는 값을 저장할 때 사용

 

2. DOM

위에서 언급했듯이, DOM 요소에 접근할 수 있도록 하는 React Hook

 

❓발생한 이슈/고민

💡해결과정

미션을 해결하기 위해 3가지 훅을 사용했다.

useState, useRef, useEffect

text 타입의 input과 password 타입 input 태그 하나씩 만들었다.

   <>
      <div>
        아이디 : <input type="text"/>
      </div>
      <div>
        비밀번호 : <input type="password"/>
      </div>
    </>

state와 ref를 선언

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

  const [id, setId] = useState('');
  const [pw, setPw] = useState('');

idChangeHandler와 pwChangeHandler 함수를 만들어 인풋에 값이 입력되면 리렌더링될 수 있도록 setState를 설정하고

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

  const pwChangeHandler = (e) => {
    setPw(e.target.value);
  };

input에 각각 레퍼런스와 value, onChange 속성을 할당했다.

   <>
      <div>
        아이디 :
        <input type="text" ref={idRef} value={id} onChange={idChangeHandler} />
      </div>
      <div>
        비밀번호 :
        <input
          type="password"
          ref={pwRef}
          value={pw}
          onChange={pwChangeHandler}
        />
      </div>
    </>

그리고 id 인풋에 입력된 값의 길이가 10자 이상이면 자동으로 비밀번호 입력창으로 포커스되도록 useEffect 훅을

이용하여 설정해 주었다.

useEffect(() => {
    if (id.length >= 10) return pwRef.current.focus();
  }, [id]);

 

+) 여기서 궁금했던 부분은 굳이 의존성 배열 없이도 컴포넌트 렌더링시마다 실행되는 오류 없이 잘 작동 되지 않을까?

하는 의문 이었다. 왜냐하면 "if (id.length >= 10)" 이라는 조건이 막아 줄 것이라 생각해서?

하지만 그렇게 만만하지 않은것..

아이디부터 입력을 한 경우에는 10자리가 넘어간 순간부터 return 되면서 useEffect가 계속 실행되는 것을

막아줬을지는 몰라도 비밀번호부터 입력한 경우의 콘솔창 결과를 보면 렌더링 될 때마다

계속해서 실행된 것을 확인할 수  있다. 이래서 의존성 배열이 필요하다는 거군..

 

🧐궁금점과 부족한 내용

강의에서는 아주 스무스하게 흘러갔지만 개인적으로 아무렇지 않지 않았던 키워드 중에

 

state와 setState의 비동기 적용?

변경사항들을 다 모아서 렌더링이 진행되기 때문에
setState 바로아래에 console 찍어봐도 원하는 값 안나올 수도 있다.?

 

리액트에서 state를 배치업데이트 후 처리하기 때문에 그렇다?


배치 업데이트? 를 좀 더 자세히 알아보았다.

 

React State Batch Update

리액트의 흥미로운 메카니즘 중의 하나는 Batch updating.

성능을 위해 여러 항목을 각각 업데이트 하는 대신 일괄적으로 업데이트 하여 컴포넌트의 렌더링 횟수를 최소화 한다.

Batch updating의 주된 아이디어는 리액트 핸들러와 생명주기의 메서드를 사용하여 얼마나 많은 메서드를 호출하든 간에

단 하나의 업데이트로 일괄 처리하여 리렌더링이 한번만 발생하도록 하는 것이다. 

이 기능은 불필요한 렌더링을 방지하기 위한 목적이고, 함수형 컴포넌트와 클래스형 컴포넌트 모두 연관되어 있다.

 

import { useState } from "react";

const App = () => {
  
  const [number, setNumber] = useState(0);
  
  return (
    <div>
  
    {/* 버튼을 누르면 1씩 플러스된다. */}
    <div>{number}</div> 
      <button
        onClick={() => {
          setNumber(number + 1); // 첫번째 줄 
          setNumber(number + 1); // 두번쨰 줄
          setNumber(number + 1); // 세번째 줄
        }}
      >
        버튼1
      </button>
	
    {/* 버튼을 누르면 3씩 플러스 된다. */}
    <div>{number}</div>
      <button
        onClick={() => {
          setNumber((previousState) => previousState + 1);
          setNumber((previousState) => previousState + 1);
          setNumber((previousState) => previousState + 1);
        }}
      >
        버튼2
      </button>
    </div>
  );
}

export default App;

Batch updating가 버튼1과 2의 결과가 다른 이유를 설명하는 근거라고 생각한다.

아래 레퍼런스를 보면 해당 내용에 대한 아주 친절한 들이 있지만 영어라 버겁다..

 

📋레퍼런스

 

React State Batch Update

Changing the state and how it affects the component renders

medium.com

 

반응형