안녕하세요, 오늘은 React의 controlled components를 활용하여 UI를 개선한 사례를 하나 소개해볼까 합니다.
input tag에 유저가 입력한 값들이 컴포넌트의 state에 실시간으로 반영되는 컴포넌트를 controlled components라고 부릅니다.
이번 시간엔 간단한 코드를 통해, controlled components 를 사용하기 전후의 UI 변화를 살펴볼게요.
저는 slider bar를 하나 만들었는데요, 제 서비스에 대한 만족도를 0에서 10까지 유저에게 받고 있습니다.
export default function ControlledComp() {
const handleSubmit = () => {
//로직은 나중에..
};
return (
<fieldset>
<form onSubmit={handleSubmit}>
<label htmlFor="slider">회원님의 만족도를 입력해주십사. </label> <br />
<h2>현재 만족도: {userSatisfactionNum}점</h2>
<input
id="slider"
type="range"
min={0}
max={10}
/>
<br />
<input type="submit" />
</form>
</fieldset>
);
}
코드는 위와 같습니다. 저는 디자인 똥손인지라, CSS 스타일링은 아예 하지 않겠습니다.
디자이너들이 보면 기겁할 디자인이지만, 어쨌든 완성되었네요.
유저는 슬라이더를 옮겨가며, 본인의 만족도를 표시할 수 있겠죠.
그런데, 디자인은 차치하고, 현재 이 컴포넌트는 유저 입장에서 굉장히 불편한 사용자 경험을 제공합니다. 내가 선택한 값이 얼만지 유저는 알 길이 없네요.
유저가 슬라이더 막대를 움직일 때마다,
화면에 유저가 선택한 값이 실시간으로 표시되도록 하면 좋겠습니다.
controlled components의 핵심은,
input태그의 value속성에 react state 변수를 넣어주고,
input태그의 onChange 이벤트 핸들러 함수에서 set state함수를 사용해주어야 한다는 것입니다.
수정된 코드는 아래와 같습니다.
import { useState } from "react";
export default function ControlledComp() {
const [userSatisfactionNum, setUserSatisfactionNum] = useState(0);
const handleRangeChange = (e) => {
setUserSatisfactionNum((prev) => e.target.value);
};
const handleSubmit = (e) => {
e.preventDefault();
};
return (
<fieldset>
<form onSubmit={handleSubmit}>
<label htmlFor="slider">회원님의 만족도를 입력해주십사. </label> <br />
<h2>현재 만족도: {userSatisfactionNum}점</h2>
<input
id="slider"
type="range"
min={0}
max={10}
value={userSatisfactionNum}
onChange={handleRangeChange}
/>
<br />
<input type="submit" />
</form>
</fieldset>
);
}
핵심인 부분만 볼까요?
<input
id="slider"
type="range"
min={0}
max={10}
value={userSatisfactionNum}
onChange={handleRangeChange}
/>
value
와 onChange
속성을 추가해주었고,
value
속성엔 userSatisfactionNum
을 전달했는데, 이는 보시다시피 state입니다.
const [userSatisfactionNum, setUserSatisfactionNum] = useState(0);
이렇게 해줌으로서, 유저가 input태그에 입력한 값들을 react state에 저장해 사용할 수 있습니다.
Controllded components가 되기 위한 첫 조건이 만족되었네요.
<input
//생략
onChange={handleRangeChange}
/>
const handleRangeChange = (e) => {
setUserSatisfactionNum((prev) => e.target.value);
};
다음으로 change이벤트 핸들러를 보니,
핸들러 함수 내부에서 set state함수인 setUserSatisfactionNum
를 사용해 이전 state의 값을 e.target.value
로 바꿔주고 있네요.
e.target.value
에는 유저가 input 태그에 입력한 값이 저장되어 있으니,
이제 실시간으로 유저가 입력한 정보가 곧바로 state가 됩니다.
<h2>현재 만족도: {userSatisfactionNum}점</h2>
마지막으로 다음과 같이 e.target.value
와 완전히 같은 값인 state userSatisfactionNum
을 화면에 띄워주면, 이는 onChange핸들러와도 연결되었기에, 실시간으로 슬라이더의 값을 보여줄 수 있습니다.
완성본을 볼까요?
슬라이더 위에, 실시간으로 만족도가 표시되어 사용자 경험이 크게 개선된 훨씬 좋은 UI가 되었네요! 이제 훌륭한 디자이너 분들의 아이디어만 기다리면 됩니다.
저는 악덕 개발자인지라, 제 서비스를 낮게 평가하는 의견 따윈 듣고 싶지 않다고 해봅시다.
이를 위해, 8점 이하의 값을 입력하면 제출 버튼을 아예 누를 수 없게 해볼게요.
HTML의 disabled 속성은, 버튼을 흐리게 하고 눌러도 동작하지 않게 만들어줍니다.
이 속성의 값에 JSX구문으로 삼항연산을 해 줄게요.
로직은 간단합니다
평점이 8 이상이면 disabled해제, 미만이면 disabled.
저희의 컴포넌트는 controlled component로서, 유저가 주는 rating의 값이 실시간으로 state가 되기에, 이 state변수를 사용해 ternary 구문을 쓰면 될 것 같습니다.
아래는 수정된 코드입니다.
<input type="submit" disabled={userSatisfactionNum >= 8 ? false : true} />
악덕 개발자의 소원이 이뤄진 순간입니다.
이 모든 것은, 유저가 입력하는 값을 실시간으로 react state에 반영하고 있기 때문에 가능했습니다.
즉, controlled component였기에 가능했습니다.
지금은 웃기자고 간단한 사례를 만들어봤지만, 실제 서비스를 개발한다 하면
다양한 비즈니스 상황에서 도움이 될 것 같습니다.
이상으로 포스팅을 마칩니다.
감사합니다
참고한 자료[meta react 공식 가이드] : https://www.youtube.com/watch?v=cd3P3yXyx30