공부/React

리액트 숙련주차 - 2

뀨뿌뀨뿌 2023. 6. 30. 18:23

1. React Hooks - useState

ⅰ.  useState 복습

  • useState는 가장 기본적인 hook이며, 함수 컴포넌트에서 가변적인 상태를 가지게 해줌
const [state, setState] = useState(initialState);
  • state가 원시 데이터타입이 아닌 객체 데이터 타입인 경우 불변성을 유지해줘야함

ⅱ.   함수형 업데이터

  • 함수형 업데이트란?
    • useState를 사용하는 방식에는 함수형 업데이트 방식도 있음
    • setState의 ()안에 수정할 값이 아니라, 함수를 넣을 수 있음
      그 함수의 인자에서는 현재 state를 가져올 수 있고, {} 안에서는 이 값을 변경하는 코드를 작성할 수 있음
// 기존에 우리가 사용하던 방식
setState(number + 1);

// 함수형 업데이트 
setState(() => {});

// 현재 number의 값을 가져와서 그 값에 +1을 더하여 반환한 것 입니다.
setState((currentNumber)=>{ return currentNumber + 1 });
  • 차이점
// 일반 업데이트 방식
// src/App.js

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); // 세번째 줄
        }}
      >
        버튼
      </button>
    </div>
  );
}

export default App;

// 함수형 업데이트 방식
// src/App.js

import { useState } from "react";

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

export default App;
  • 일반 업데이트 방슥은 첫번째 ~ 세번째 줄에 있는 setNumber가 각각 실행되는 것이 아니라, 배치(batch)로 처리함 => 배치 업데이트
    => 리액트에서는 컴포넌트를 변경하는방법 즉, 렌더링을 하기 위해서 state를 파악하는데 그 state를 파악하는 방법은 배치 업데이트 
          배치 업데이트는 한꺼번에 변경된 내용들을 모아서 한번만 반영을 하기 때문에 setNumber을 3번을 해도 동일한 내용이므로 한개로 인식하여 한번만 처리됨
  • 함수형 업데이트 방식은 3번을 동시에 명령을 내리지만, 그 명령을 모아 순차적으로 각각 1번씩 실행시킴

ⅲ. 리액트는 왜 배치 업데이트가 되도록 설계했을까?

  • 리액트 환경에서 렌더링이 잦은건 좋은게 아님 => 성능에 이슈가 발생함
  • 불필요한 렌더링을 피하기 위해서 한꺼번에 요청사항을 모아서 한번만 처리되는게 렌더링을 줄일 수 있는 좋은 방법
  • 불필요한 리 렌더링을 방지(렌더링 최적화)하기 위해 즉, 리액트의 성능을 위해 한꺼번에 state 업데이트를 한다고함
리액트는 성능을 위해 setState()를 단일 업데이트(batch update)로 한꺼번에 처리할 수 있습니다.
useState의 업데이트 방식은 2가지 방식이 있으며, 각각 다르게 동작함
useState로 원시데이터가 아닌 데이터를 변경할때는 불변성을 유지해야함

2. useEffect

ⅰ. useEffect란?

  • useEffect 사용
    • useEffect는 리액트 컴포넌트가 렌더링될 때마다 특정 작업을 수행하도록 설정할 수 있는 리액트 Hook
    • 컴포넌트가 화면에 보여졌을 때 내가 무언가를 실행하고 싶다면 또는 어떤 컴포넌트가 화면에서 사라졌을 때 무언가를 실행하고 싶다면 useEffect를 사용함
  • useEffect의 기초
import React, { useEffect } from 'react'

const App = () =>  {
  useEffect(() => {
    console.log("hello useEffect!")
  })
  return (
    <div>App</div>
  )
}

export default App

📚 App 컴포넌트가 화면에 렌더링될 떼 useEffect 안에 있는 console.log 가 실행됨.
📍 컴포넌트가 렌더링 될 때 실행된다 이게 바로 useEffect 핵심기능!!!!

  • useEffect와 리렌더링(re-rendering)
    • useEffect는 useEffect가 속한 컴포넌트가 화면에 렌더링 될 때 실행되는데 이런 useEffect의 특징에 의해 의도치 않는 동작을 경험할 수 있음 
import React, { useEffect, useState } from "react";

const App = () => {
  const [value, setValue] = useState("");
  useEffect(() => {
    console.log("hello useEffect!");
  });
  return (
    <div>
      <input
        type="text"
        value={value}
        onChange={(e) => {
          setValue(e.target.value);
        }}
      />
    </div>
  );
};

export default App;

📚 input에 어떤 값을 입력하면 useEffect가 계속 실행됨
전체 흐름
1. input 에 값을 입력
2 . value, 즉 state가 변경됨
3. state가 바뀌었기 때문에  => App컴포넌트가 리렌더링됨
4. 리렌더링이 됨 => useEffect가 다시 실행됨
1 - 4 지속적으로 반복됨 => 이러한 것을 해결하기 위해서 의존성 배열이라는 것이 필요함

ⅱ. 의존성 배열(dependency array)

  • 의존성 배열이란?
    • useEffect에는 의존성 배열이라는 것이 있음.
    • 이 배열에 값을 넣으면 그 값이 바뀔 때만 useEffect를 실행한다!
// useEffect의 두번째 인자가 의존성 배열이 들어가는 곳 입니다.
useEffect(()=>{
	// 실행하고 싶은 함수
}, [의존성배열])
  • 의존성 배열이 빈 배열인 경우
    • 의존성 배열안에는 어떠한 값을 넣지 않았으니 useEffect는 처음에 딱 한번만 실행되고 그 이후로는 어떤일이 일어나도 실행이 되서는 안됨
    • 어떠한 함수를 컴포넌트가 렌더링 될 때 단 한번만 실행하고 싶으면 의존성 배열을 [] 빈 상태로 넣으면 됨
// src/App.js

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

const App = () => {
  const [value, setValue] = useState("");
  useEffect(() => {
    console.log("hello useEffect");
  }, []); // 비어있는 의존성 배열

  return (
    <div>
      <input
        type="text"
        value={value}
        onChange={(event) => {
          setValue(event.target.value);
        }}
      />
    </div>
  );
}

export default App;

의존성 배열에 값이 빈경우

  • 의존성 배열에 값이 있는 경우
import React, { useEffect, useState } from "react";

const App = () => {
  const [value, setValue] = useState("");
  useEffect(() => {
    console.log(`hello useEffect!: ${value}`);
  }, [value]);
  return (
    <div>
      <input
        type="text"
        value={value}
        onChange={(e) => {
          setValue(e.target.value);
        }}
      />
    </div>
  );
};

export default App;

- value는 state이고 우리가 input을 입력할 때마다 그 값이 변하게 되므로 useEffect도 게속 실행됨!

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

 ⅲ. clean up

  • 클린업이란?
    • 맨 처음에 화면에 뭔가 나왔을 때도 동작을 하지만 화면에서 없어질 때도 동작을 한다라는 것이 클린 업(clean up)이라고 함
  • 클린업 하는 방법
// src/App.js

import React, { useEffect } from "react";

const App = () => {

	useEffect(()=>{
		// 화면에 컴포넌트가 나타났을(mount) 때 실행하고자 하는 함수를 넣어주세요.

		return ()=>{
			// 화면에서 컴포넌트가 사라졌을(unmount) 때 실행하고자 하는 함수를 넣어주세요.
		}
	}, [])

	return <div>hello react!</div>
};

export default App;
useEffect는 화면에 컴포넌트가 mount 또는 unmount 됐을 때 실행하고자 하는 함수를 제어하게 해주는 훅
의존성 배열을 통해 함수의 실행 조건을 제어할 수 있음
useEffect에서 함수를 1번만 실행시키고자 할때는 의존성 배열을 빈 배열로 둠

3.  useRef

  • 저장공간의로서의 useRef, DOM요소 접근 방법으로서의 useRef 활용 방법!!

ⅰ. useRef란?

  • DOM 요소에 접근할 수 있도록 하는 React Hook
  • HTML 과 JS를 사용했을 때 우리는 특정 DOM을 선택하기 위해서 getElementById, querySelector를 이용함
  • 리액트에서도 DOM을 선택해야 할 상황이 생기기 마련  => 이러한 경우 useRef hook을 상용함
  • 또한 변수처럼 쓰기 위해서도 useRef를 사용함
  • 사용 방법
import { useRef } from "react";

const App = () => {
  // ref : reference
  const ref = useRef("초기값")
  console.log("ref", ref)
  return <div></div>
};

export default App;

ref 초기값

  • ref값 변경하기
import { useRef } from "react";

const App = () => {
  // ref : reference
  const ref = useRef("초기값")
  console.log("ref", ref)

  ref.current = "변경값"
  console.log("ref2", ref)
  return <div></div>
};

export default App;

ref값 변경하기

  • 이렇게 설정된 ref값은 컴포넌트가 계속해서 렌더링 되어도 unmount전까지 값을 유지함
  • 위에 특징때문에 2가지 용도로 사용됨
    • 저장공간
      • state와 비슷한 역활을 함 => 다만 state는 변화가 일어나면 렌더링이 일어나고 내부변수들은 초기화됨
      • ref에 저장한 값은 렌더링을 일으키지 않음. => 즉, ref의 값 변화가 일어난도 렌더링으로 인해 내부 변수들이 초기화 되는것을 막을 수 있음
      • 컴포넌트가 100번 렌더링 => ref에 저장한 값은 유지됨
      • 정리
        📍 state는 리렌더링이 꼭 필요한 값을 다룰 때 쓰면됨
        📍 ref는 리렌더링을 발생시키지 않는 값을 저장할 떼 사용
    • DOM
      • 렌더링이 되자마자 특정 Input이 focusing 돼야 한다면 useRef를 사용하면 됨

ⅱ. state와 ref 차이점

import { useRef, useState } from "react";

const style = {
  border: "1px solid black",
  margin: "10px",
  padding: "10px",
};

const 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의 차이점

📌📌 내부 변수는 let 키워드로 선언해서 변수를 사용하면 안되는지
- let 키워드를 사용하게 되면 렌더링시 다시 변수가 초기화 됨 => 함수라서
리렌링이 된다 = 함수가 다시 호출된다 = 내부 변수가 다시 쫙 초기화됨

ⅲ. DOM 접근

  • input 태그에는 ref라는 속성이 있는데 이걸 통해 해당 DOM에 요소로 접근할 수 있음
import { useEffect, useRef } from "react";

const App = () => {
  const idRef = useRef("");
  const pwRef = useRef("");

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

export default App;

ⅳ. 위에 코드에서 아이디가 10자리 입력되면 자동으로 비밀번호 필드로 이동하도록 만들기!

  • 내가 만든 코드
import { useEffect, useRef, useState } from "react";

const App = () => {
  const idRef = useRef("");
  const pwRef = useRef("");

  const [id, setId] = useState("");
  const handleIdChage = (e) => {
    setId(e.target.value);
  };

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

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

export default App;
  • 강의에서 알려준 내용
import { useEffect, useRef, useState } from "react";

const App = () => {
  const idRef = useRef("");
  const pwRef = useRef("");

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

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

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

export default App;

 

'공부 > React' 카테고리의 다른 글

리액트 숙련주차 - 4  (0) 2023.07.10
리액트 숙련주차 - 3  (0) 2023.07.05
리액트 숙련주차 - 1  (0) 2023.06.30
리액트 입문주차 1주차 - 5  (0) 2023.06.29
리액트 입문주차 1주차 - 4  (0) 2023.06.27