패스트 캠퍼스 MGS 3기 - 5월 20일(useState, useRef)

JY·2022년 5월 20일
0

useRef


기존 바닐라 자바스크립트에서는 input element가 필요하면 getElementByID, querySelector 등을 이용해 해당 DOM을 가져와서 사용한다.
리액트에서는 useRef라는 것을 사용한다. 여러 HTML 엘리먼트 중에서도 <input>을 제어할 때 많이 사용된다. usestate는 값이 변경되었을 때, 함수 컴포넌트를 리렌더링하지만, useRef는 리렌더링이 발생하지 않는다.

useRef의 일반적인 사용법

  • ref를 넘겨주면, 해당 DOM element를 current에 담아준다.
    • input.current.value, input.current.focus()
  • 대개 기본값으로 null을 넣는다.
    • const input = useRef(null);

useRef?

  • ref는 DOM을 담을 때만 사용하나? (❌)
  • ref는 값이 바뀌어도 컴포넌트가 리렌더링 되지 않는다? (⭕)
  • 리액트에 의한 리렌더가 아닌, 실제 Documnet에 존재하는 element를 직접 접근해서 수정하는 것이다.
  • 리액트 state로는 관리할 수 없는 경우에만 사용하는 것이 적절하다. (focus() 등)

실습

👩‍💻 01


input이 렌더되기 전까지 input.currentnull이므로 if문으로 유효한지 체크해야 한다.

// useRef를 사용하여, Click to Reset 버튼을 클릭하면
// input의 value를 초기화하도록 만들기

import React, {useRef} from 'react'

export default function Practice1() {
  const input = useRef(null);
  const handleClick = () => {
    // 화면에 그려지기 전까지는 input.current가 null이다.
    // input.current가 유효한지 체크하는 것!
    if (input.current) {
      console.log(input.current);
      console.log(input.current.value);
      input.current.value = null;
      input.current.disabled = true;
    }
  }
  return (
    <div>
      <input type="text" ref={input} />
      <button type="button" onClick={handleClick}>Click to Reset</button>
    </div>
  )
}

👩‍💻 02-1

Uncontrolled Input


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

export default function Practice2() {
  const input = useRef(null);
  const [value, setValue] = useState("");

  const handleClick = () => {
    if (input.current) {
      input.current.value = null;
      setValue(null);
    }
  }

  const handleChangeInput = (event) => {
    setValue(event.target.value);
  }

  return (
    <div>
      <div>현재 value는 {value}입니다.</div>
      <input type="text" ref={input} onChange={handleChangeInput} />
      <button type="button" onClick={handleClick}>Click to Reset</button>
    </div>
  )
}

🤔 만일, setValue('')이 누락되면?
렌더가 일어나지 않으므로 Reset 버튼을 눌러도 text 부분의 value 값이 그대로 남아있을 것이다.

🤔 반대로, input.current.value = ''이 누락되면?
리액트 state에 있는 value만 업데이트 해준 것이므로 input의 value 값이 그대로 남아있을 것이다.

이처럼 둘 중에 하나라도 업데이트 하지 않으면 Bug가 발생한다. 👉 불편하다!

🤔 그럼 그냥 getElementByID, querySelector 사용하면 안 되나?
getElementByID, querySelector 등은 DOM 트리를 돌면서 element 하나하나 검사해야 한다. 브라우저 입장에서 메모리를 소모하는 것이다. (부하 발생)
Ref로 변수에 대입만 해주는 것은 위와 같은 과정을 생략할 수 있는 것!


👩‍💻 02-2

Controlled Input

하나의 state를 이용해서 여러 가지를 컨트롤 하는 것이다.
02-1(Uncontrolled)을 다음처럼 prop으로 value를 넘겨주어서 Controlled로 바꿀 수 있다. (useState를 통해 업데이트 된 valueinputvalue로 넘겨주는 것!)

👩‍💻 03

그럼 useRef는 필요 없는 거 아니야?
❌ 아니다.

Controlled와 Uncontrolled는 상호배타적이지만, ref는 필요하다면 어디서는 사용할 수 있다.
또한, disabled과 같은 경우 true/false를 prop으로 넘겨서 제어할 수 있다. (Ref를 사용하지 않아도 됨)
하지만, focus()를 해야 하는 경우엔 prop으로 제어할 수 없어서 Ref를 사용할 수밖에 없다.

// useRef를 사용하여, Click to Reset 버튼을 클릭하면
// input의 value를 초기화하도록 만들기
// input 위에 현재 value를 알려주는 문구 추가하기

import React, {useState} from 'react'

export default function Practice3() {
  const input = React.useRef(null);
  const [value, setValue] = useState("");

  const handleClick = (props) => {
    setValue('');
    if(input.current) {
      input.current.focus();
    }
  }

  const handleChangeInput = (event) => {
    setValue(event.target.value);
  }

  return (
    <div>
      <div>현재 value는 {value}입니다.</div>
      <input type="text" ref={input} value={value} onChange={handleChangeInput} />
      <button type="button" onClick={handleClick}>Click to Reset and Focus!</button>
    </div>
  )
}

👩‍💻 04

useState, useRef를 이용해 회원가입 Form 유효성 검사 구현하기

  • id는 6글자 이상 20글자 이하인 경우 유효
  • password는 12글자 이상 20글자 이하인 경우 유효
  • 유효하지 않는 input 밑에 "유효하지 않은 ~~입니다." 출력

    👉 값 입력 시 처리되어야 하므로 onChangehandleChangeInput 컴포넌트를 주어 이 컴포넌트에서 처리하였다.
    1. input에 값이 입력될 때, useState로 입력된 값을 각각 id, password에 저장한다.
    2. 삼항연산자 중첩. 먼저, id에 값이 있다면 유효한 조건에 맞는지 체크하고, 값이 없을 때는 메시지가 출력되지 않도록 했다.(null) id에 값이 있을 때, 유효한 조건에 맞는지 체크하는데, 조건에 맞다면 메시지를 출력하지 않고(null) 조건에 맞지 않다면 메시지를 출력한다.(idMsg)
  • id와 password가 둘 다 비어있으면 회원가입 버튼 disable 처리

    👉 삼항연산자를 사용하여 id와 password 모두 값이 있을 때만 disabledfalse가 되도록 한다. (disabled={(id || password) ? false : true})
  • 유효하지 않은 input이 존재하는 경우 회원가입 버튼 클릭 시 에러 alert를 띄워주고, 해당 input reset하고, focus 시켜주기

    👉 버튼을 클릭했을 때, 발생해야 하므로 handleClick에서 처리하였다.
    1. 유효 조건에 맞지 않다면 idMsgalert으로 띄워주었다.
    2. 그리고 useRef를 통해 해당 inputfocus했다. (idRef.current.focus();)
    3. 이때, input에 입력한 값을 초기화하기 위해 setter 함수를 이용했다. (setId('');, <input value={id} />)
      idRef.current.value='';를 사용하면 input에 입력된 값은 사라지지만, 옆에 문구(idMsg)가 사라지지 않는다!

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

export default function Practice4() {
  const [id, setId] = useState("");
  const [password, setPassword] = useState("");
  const idRef = useRef(null);
  const passwordRef = useRef(null);
  const idMsg = "유효하지 않은 id입니다.";
  const passwordMsg = "유효하지 않은 password입니다.";
  const idCheck = (id.length >= 6 && id.length <= 20);
  const passwordCheck = (password.length >= 12 && password.length <= 20);

  const handleChangeInput = (e) => {
    if(e.target.name === "id") {
      setId(e.target.value);
    }
    if(e.target.name === "password") {
      setPassword(e.target.value);
    }
  }

  const handleClick = () => {
    if ( !idCheck ) {
      alert(idMsg);
      setId('');
      idRef.current.focus();
    }
    else if ( !passwordCheck ) {
      alert(passwordMsg);
      setPassword('');
      passwordRef.current.focus();
    }
    else alert('회원가입 성공!');
  };

  return (
  <div>
    <div>
      <input type="text" name='id' ref={idRef} value={id} onChange={handleChangeInput} placeholder='6글자 이상 20글자 이하' />
      {id ? 
        idCheck ? null : idMsg
      : null}
    </div>
    <div>
      <input type="text" name='password' ref={passwordRef} value={password} onChange={handleChangeInput} placeholder='12글자 이상 20글자 이하' />
      {password ? 
        passwordCheck ? null : passwordMsg
      : null}
    </div>
    <button type="button" onClick={handleClick} disabled={
      (id || password) ? false : true 
      }>회원가입</button>
  </div>
  );
}
profile
🙋‍♀️

0개의 댓글