[React] 폼과 이벤트 제어하기

youngseo·2022년 7월 27일
0

REACT

목록 보기
20/52
post-thumbnail

Form

React에서의 Form

React공식문서-Form
대부분 경우에 폼을 구현하는데 제어 컴포넌트를 사용하는 것이 좋습니다. 제어 컴포넌트에서 폼 데이터는 React 컴포넌트에서 다루어집니다.

input, textarea, select 등의 form 요소들은 태그 그 자체들로 사용자의 입력을 기반으로 데이터를 가지고 있습니다. 이러한 폼 태그들을 리액트에서도 그대로 사용을 할 수 있습니다.

input

아래 예시를 보겠습니다. 예시는 label과 input태그를 가지고 있습니다.

import React from 'react'

export default function SimpleForm() {
  const handleChange = (e) => {
    console.log(e.target.value)
  }

  return (
    <>
      <label>닉네임: </label>
      <input type="text" name="nickname" onChange={handleChange} />
    </>
  )
}

이 컴포넌트를 리액트에서 한번 렌더링해보도록 하겠습니다.

아래와 같이 input태그에 값이 찍힐 때마다 onChange이벤트에 의해 콘솔에 값이 찍히고 있는 것을 확인할 수 있습니다

👀input태그의 값은 누가 관리하나요?

  • input태그는 React 코드 내에 통해 짜여지기는 했지만, input태그가 가지고 있는 value는 React가 아닌 input태그가 관리를 하고 있습니다.

SimpleForm.js

import React from 'react'

export default function SimpleForm() {
 const handleChange = (e) => {
   console.log(e.target.value)
 }
   
//현재 위치에서는 input의 value(e.target.value)를 가져와 사용할 수 없습니다.
 
 const handleSubmit = (e) => {
	//form태그 내의 다른 input들의 값을 가져와 사용하기가 어렵습니다.
 }

 return (
   <form onSubmit={handleSubmit}>
     <label>닉네임: </label>
     <input type="text" name="nickname" onChange={handleChange} />
     <input type="submit" value="제출" />
   </form>
 )
}

따라서 위 예제처럼 input태그에서 value를 관리하기 때문에 리액트 코드 내의 원하는 위치에서 input창의 e.target.value를 가져와 사용하기가 조금 힘든 상황입니다.

마찬가지로 input[type=submit]버튼이 눌렸을 때 form태그의 onSubmit에 걸려있는 handleSubmit함수 내부에서 input태그들이 가지고 있는 값을 가져와 사용을 하기가 어렵습니다.

🐥리액트에서 input값을 관리하려면?

리액트에서 useState 훅을 이용하는 등 값들을 관리할 수 있는 작업이 필요합니다.

방법 1. 제어 컴포넌트 (useState훅 & e.target.value)

HTML에서 폼 엘리먼트는 일반적으로 사용자의 입력을 기반으로 자신의 state를 관리하고 업데이트합니다. React에서는 변경할 수 있는 state가 일반적으로 컴포넌트의 state 속성에 유지되며 setState()에 의해 업데이트됩니다.

하지만 리액트 내에서 form태그들을 사용했을 경우 DOM에서도 관리를 하고, React에서도 관리를 하는 상황이 되어버립니다.

그렇기 때문에 React안에서 html의 폼 엘레먼트까지 제어할 수 있도록 React state를 “신뢰 가능한 단일 출처 (single source of truth)“로 만들어 두 요소를 결합합니다.
이러한 방식으로 React에 의해 값이 제어되는 입력 폼 엘리먼트를 “제어 컴포넌트 (controlled component)“라고 합니다.
(제어 컴포넌트의 경우 value어트리뷰트를 기반으로 하고 있습니다.)

import React, {useState} from 'react'

export default function SimpleForm() {

  const [nickname, setNickName] = useState('') //1.
  
  const handleChange = (e) => {
    setNickName(e.target.value)//setState에서 e.target.value을 지정하면
  }
  const handleSubmit = (e) => {
    //submit의 기본 기능이 동작하지 않도록 제어
    e.preventDefault()
     //✅3. 위에서 setState로 관리하고 있기때문에 여기서도 input값을 가져와 사용할 수 있습니다.
    alert(nickname)
  }
  
  return (
    <form onSubmit={handleSubmit}>
      <label>닉네임: </label>
      <input 
        type="text" 
        name="nickname" 
        onChange={handleChange} 
        value={nickname} //✅2. 
       />
      <input type="submit" value="제출" /> 
    </form>
  )
}

1-2. 다중 입력 제어하기

여러 input 엘리먼트를 제어해야할 때, 각 엘리먼트에 name 어트리뷰트를 추가하고 event.target.name 값을 통해 핸들러가 어떤 작업을 할 지 선택할 수 있게 해줍니다.

import React, {useState} from 'react'

export default function SimpleForm() {

  const [nickname, setNickName] = useState('')
  const [password, setPassWord] = useState('')
  
  const handleChange = (e) => {
    
    //e.target.name이 nickname일수도, password일 수도 있으므로
    if(e.target.name === "nickname") return setNickName(e.target.value)
    return setPassWord(e.target.value)
  }
  
  const handleSubmit = (e) => {
    e.preventDefault();
    alert(`nickname: ${nickname}, password: ${password} `)
  }
  
  return (
    <form onSubmit={handleSubmit}>
      <label>닉네임: </label>
      <input 
        type="text" 
        name="nickname" 
        onChange={handleChange} 
        value={nickname}
      />
      <label>패스워드: </label>
      <input 
        type="text" 
        name="password" 
        onChange={handleChange} 
        value={password}
      />
      <input type="submit" value="제출" />
    </form>
  )
}

그런데 만약 여러개의 input 태그를 다뤄야하는 경우 state값도 늘어나게 됩니다. 마찬가지로 if구문도 계속 늘어나게 될 것입니다.

따라서 이를 아래와 같이 각각의 state로 만드는 것이 아닌 하나의 객체로 관리함으로써 조금 더 효율적으로 사용을 할 수 있습니다.

import React, {useState} from 'react'

export default function SimpleForm() {

 //✅각각의 state로 만드는 것이 아닌 하나의 객체로 관리를 해줍니다.
  const [userInputs, setUserInputs] = useState({
    nickname: '',
    password: ''
  })
  
  const handleChange = (e) => { //✅
    setUserInputs({
      ...userInputs, 
      [e.target.name] : e.target.value 
    })
  }
  
  const handleSubmit = (e) => {
    e.preventDefault();
    const { nickname, password } = userInputs //✅
    alert(`nickname: ${nickname}, password: ${password} `)
  }
  return (
    <form onSubmit={handleSubmit}>
      <label>닉네임: </label>
      <input 
        type="text" 
        name="nickname" 
        onChange={handleChange} 
        value={userInputs.nickname}
      />
      <br/>
      <label>패스워드: </label>
      <input 
        type="text" 
        name="password" 
        onChange={handleChange} 
        value={userInputs.password}
      />
      <input type="submit" value="제출" />
    </form>
  )
}

이와 같이 데이터를 변경할 수 있는 모든 방법에 대해 이벤트 핸들러를 작성하고 React에 연결한 후 모든 입력 상태를 연결해 제어를 합니다.

방법 2. 비제어 컴포넌트(ref)

제어 컴포넌트에서 폼 데이터는 React 컴포넌트에서 다루어집니다. 대안인 비제어 컴포넌트는 DOM 자체에서 폼 데이터가 다루어집니다.

DOM자체에서 다룬다면, input의 값들을 handlechange함수가 아닌 그 외부에서 다루려면 어떻게해야하는지에 대한 문제를 해결해야합니다. 이를 위해 ref 방법을 제공합니다.

//클래스 컴포넌트
import {createRef} from "react"

//함수 컴포넌트
import {useRef} from "react"

ref를 사용하는 방법은 위와 같이 클래스 컴포넌트와 함수 컴포넌트가 조금 다릅니다.

App.js

import React from "react"

export default function UnControlledForm() {
  const handleChange = (e) => {
    console.log(e.target.value)
  }

  return (
    <>
      <label>닉네임 :</label>
      <input type="text" name="nickname" onChange={handleChange}/>
    </>
  )
}

UnControlledForm.js

//함수에서는 useRef로 가져올 수 있습니다.
import React, { useRef } from "react"

//ref활용 => 비제어 컴포넌트로 From을 다뤄보자
export default function UnControlledForm() {
  const inputRef = useRef() //✅
  const handleChange = (e) => {
    console.log(e.target.value)
  }

  console.log(inputRef)
  console.log(inputRef.current)

  return (
    <>
      <label>닉네임 :</label>
      <input type="text" name="nickname" onChange={handleChange} ref={inputRef}/>
    </>
  )
}

useRef로 만든 객체에는 Current라는 키를 가지고 있는 것을 확인할 수 있습니다. 이렇게 만든 ref객체를 ref={inputRef} 태그에 다음과 같이 붙이는 경우 current가 가르키는 값에 해당 태그가 들어가게 됩니다.

이번에는 한번 form태그를 붙인 후 handleSubmit함수를 통해 input태그의 값을 한번 가져올 수 있는지 확인해보도록 하겠습니다.

//ref활용 => 비제어 컴포넌트로 From을 다뤄보자
export default function UnControlledForm() {
  const inputRef = useRef()

  const handleSubmit = (e) => {
    e.preventDefault();
    alert(inputRef.current.value)
  }

  console.log(inputRef)

  return (
    <form onSubmit={handleSubmit}><label>닉네임 :</label>
      <input 
        type="text" 
        name="nickname" 
        ref={inputRef}
      />
      <input type="submit" value="제출" />
    </form>
  )
}

수정한 값이 올바르게 뜨는 것을 나오는 것을 확인할 수 있습니다. 이렇게 비제어 컴포넌트 방식으로, DOM에 있는 값을 가져와 사용을 할 수 있습니다.

0개의 댓글

관련 채용 정보