1. 참조를 이용하는 방법
useRef()
훅은 컴포넌트 생명주기 동안 사용할 수 있는 ref 객체를 반환해 줍니다.
이 객체는 current
프로퍼티에 렌더링에 필요하지 않은 값을 저장해 두고, 필요할 때마다 참조할 수 있도록 해줍니다.
CreateForm.js는 아이디와 패스워드를 저장할 수 있는 ref 객체(loginId
, password
)를 <input>
엘리먼트의 ref
속성에 추가해주고 있습니다. submit 이벤트가 발생하면 ref
객체의 current.value
에 있는 값을 id
, pw
에 각각 저장한 다음 onAccess
함수를 통해서 상위 컴포넌트로 올려 보내고 다시 초기화시켜 줍니다. 만약 입력한 아이디를 유지하고 싶으면 loginId.current.value = "";
부분을 삭제하면 됩니다.
이 부분은 DOM 노드의 값을 직접 변경해 주는 명령형 코드입니다. 이렇게 DOM 그 자체를 통해서 폼의 값을 다루고 있으므로 CreateForm은 제어되지 않는 컴포넌트(uncontrolled component)입니다. 반대로 제어 가능한 컴포넌트의 경우 리액트 컴포넌트를 통해서 폼 데이터를 핸들링할 수 있습니다. 일반적으로는 제어 가능한 컴포넌트의 사용이 권장되나, 리액트 밖에서 폼에 접근해야 하는 경우 제어되지 않는 컴포넌트를 사용할 수 있습니다.
참조를 이용해서 아이디와 비밀번호로 간단히 회원가입을 할 수 있는 폼을 만들어보도록 하겠습니다.
<CreateForm.js>
import React, { useRef } from "react";
export default function CreateForm({ onAccess = f => f }){
const loginId = useRef();
const password = useRef();
const submit = e => {
e.preventDefault();
const id = loginId.current.value;
const pw = password.current.value;
onAccess(id, pw);
loginId.current.value = "";
password.current.value = "";
};
return (
<form onSubmit={submit}>
<input ref={loginId} type="text" required/>
<input ref={password} type="password" required/>
<button>submit</button>
</form>
);
}
CreateForm의 부모 컴포넌트인 App.js에서는 입력된 id
, pw
를 전달받아 화면에 보여주고 있습니다.
<App.js>
import './App.css';
import {useState} from 'react';
import CreateForm from './CreateForm';
export default function App() {
return (
<CreateForm
onAccess={(id, pw) => alert(id + ":" + pw)}
/>
);
}
2. 상태를 통해서 폼 처리하기
useState()
를 사용해서 상태를 관리하는 부분을 추가했습니다. 전체적인 구조는 비슷하지만 여기서는 <input>
의 value
프로퍼티에 상태변수인 loginId
, password
를 넣어주고 있습니다.
<CreateForm.js>
import React, { useState } from "react";
export default function CreateForm({ onAccess = f => f }){
const [loginId, setLoginId] = useState("");
const [password, setPassword] = useState("");
const submit = e => {
e.preventDefault();
onAccess(loginId, password);
setLoginId("");
setPassword("");
};
return (
<form onSubmit={submit}>
<input
value={loginId}
onChange={e => setLoginId(e.target.value)}
type="text"
required
/>
<input
value={password}
onChange={e => setPassword(e.target.value)}
type="password"
required
/>
<button>submit</button>
</form>
);
}
이 경우는 리액트가 폼의 상태를 제어하게 됩니다. 주의해야 할 점은 사용자가 글자 하나를 입력할 때마다 렌더링이 이루어지기 때문에 이 컴포넌트 내에서 복잡한 연산을 수행할 경우 문제가 될 수 있다는 점입니다.
3. <input>이 엄청나게 많은 경우
<input>
에는 value
와 onChange
프로퍼티가 공통적으로 들어가고 있습니다. 지금이야 2개뿐이지만 공공기관에 어떤 신청서 같은 것을 작성해 제출해야 할 경우 수 십 개의 <input>
이 필요하게 됩니다. 그리고 중복되는 부분을 수십 번 붙여 넣기 해야 합니다. 커스텀 훅을 만들어서 이 부분을 보다 깔끔하게 만들어줄 수 있습니다.
useInput.js는 내부적으로 상태 value
를 관리하고 있습니다.
useInput()
훅이 반환하는 것은 첫 번째 원소인 value
와 onChange
프로퍼티, 그리고 두 번째 원소로 값을 init
으로 초기화해 주는 함수입니다.
<useInput.js>
import { useState } from "react";
export const useInput = init => {
const [value, setValue] = useState(init);
return [
{value, onChange: e => setValue(e.target.value)},
() => setValue(init)
];
}
위에서 작성한 소스를 아래와 같이 useInput()
을 이용하도록 바꿔줍니다.
<CreateForm.js>
import React, { useRef, useState} from "react";
import { useInput } from "./useInput.js";
export default function CreateForm({ onAccess = f => f }){
const [idProps, resetId] = useInput("");
const [pwProps, resetPw] = useInput("");
const submit = e => {
e.preventDefault();
onAccess(idProps.value, pwProps.value);
resetId();
resetPw();
};
return (
<form onSubmit={submit}>
<input
{...idProps}
type="text"
required
/>
<input
{...pwProps}
type="password"
required
/>
<button>submit</button>
</form>
);
}
value
와 onChange
프로퍼티를 구조 분해해서 다수의 <input>
에 넣어줍니다.
그리고 submit에서 초기화를 할 때는 resetId
, resetPw
함수를 사용합니다.
4. 폼으로 입력받은 값을 상태로 저장하기
앞서 App.js 에서는 입력된 값을 경고창을 통해 확인해주기만 했습니다. 이제 입력된 아이디와 비밀번호를 users
상태에 추가해 주겠습니다. 초기값은 user-data.json
에 저장된 내용입니다.
<App.js>
import React, {useState} from 'react';
import CreateForm from './CreateForm';
import userData from './user-data.json';
export default function App() {
const [users, setUsers] = useState(userData);
return (
<CreateForm
onAccess={(id, pw) => {
const newUsers = [
...users,
{
date: "230525",
id,
pw
}
];
setUsers(newUsers);
}}
/>
);
}
참고
러닝리액트 2판 - O'REILLY
'Front-end > React' 카테고리의 다른 글
React 시작하기 (2) | 2022.09.20 |
---|
댓글