react는 ui를 조작하는 선언적인 방법을 제공합니다. ui를 개별적으로 직접 조작하는 대신 컴포넌트가 있을 수 있는 다양한 상태를 기술하고, 사용자 입력에 반응하여 각 상태들 사이를 전환합니다. 이는 디자이너가 ui를 바라보는 방식과 유사합니다.
🎈선언형 UI와 명령형 UI의 차이점
UI 인터렉션을 디자인할 때 사용자의 행동에 따라 UI가 어떻게 변하는지
폼에 무언가를 입력하면 submit 버튼이 활성화될 것입니다.
제출 버튼을 누르면 폼과 버튼이 비활성화되고 스피너가 나타날 것입니다.
네트워크 요청이 성공한다면 form은 숨겨질 것이고 thank you 메세지가 나타날 것입니다.
네트워크 요청이 실패한다면 오류 메시지가 보일 것이고 form은 다시 활성화 될 것입니다.
명령형 프로그래밍에서 위의 내용은 인터랙션은 구현하는 방법에 직접적으로 해당합니다. 방금 일어난 일에 따라 UI를 조작하기 위한 정확한 지침을 작성해야 합니다.
컴퓨터에게 스피너부터 버튼까지 각 요소에 명령을 내려 컴퓨터에 어떻게 UI를 업데이트할 내용을 지시하는지 -> 명령형
브라우저 빌트인 DOM 사용함
async function handleFormSubmit(e) {
e.preventDefault();
disable(textarea);
disable(button);
show(loadingMessage);
hide(errorMessage);
try {
await submitForm(textarea.value);
show(successMessage);
hide(form);
} catch (err) {
show(errorMessage);
errorMessage.textContent = err.message;
} finally {
hide(loadingMessage);
enable(textarea);
enable(button);
}
}
function handleTextareaChange() {
if (textarea.value.length === 0) {
disable(button);
} else {
enable(button);
}
}
function hide(el) {
el.style.display = 'none';
}
function show(el) {
el.style.display = '';
}
function enable(el) {
el.disabled = false;
}
function disable(el) {
el.disabled = true;
}
function submitForm(answer) {
// Pretend it's hitting the network.
return new Promise((resolve, reject) => {
setTimeout(() => {
if (answer.toLowerCase() == 'istanbul') {
resolve();
} else {
reject(new Error('Good guess but a wrong answer. Try again!'));
}
}, 1500);
});
}
let form = document.getElementById('form');
let textarea = document.getElementById('textarea');
let button = document.getElementById('button');
let loadingMessage = document.getElementById('loading');
let errorMessage = document.getElementById('error');
let successMessage = document.getElementById('success');
form.onsubmit = handleFormSubmit;
textarea.oninput = handleTextareaChange;
<form id="form">
<h2>City quiz</h2>
<p>
What city is located on two continents?
</p>
<textarea id="textarea"></textarea>
<br />
<button id="button" disabled>Submit</button>
<p id="loading" style="display: none">Loading...</p>
<p id="error" style="display: none; color: red;"></p>
</form>
<h1 id="success" style="display: none">That's right!</h1>
<style>
* { box-sizing: border-box; }
body { font-family: sans-serif; margin: 20px; padding: 0; }
</style>
-> 복잡한 시스템에서 관리하기 어려움
react에서는 직접 UI를 조작하지 않습니다. 표시할 내용을 선언하면 react가 UI를 업데이트할 방법을 알아냅니다.
🎈UI를 선언적인 방식으로 생각하기
1. 컴포넌트의 다양한 시각적 상태를 식별합니다.
2. 상태 변화를 촉발하는 요소를 파악합니다.
3. useState를 사용하여 메모리의 상태를 표현합니다.
4. 비필수적인 state 변수를 제거합니다.
5. 이벤트 핸들러를 연결하여 state를 설정합니다.
🎈 1. 컴포넌트의 다양한 시각적 상태 식별하기
empty: form의 submit 버튼이 비활성화
Typing: form의 submit 버튼이 활성화
submitting: form은 완전히 비활성화되어있고 spinner가 표시됨
success: form 대신 thank you 메시지가 표시됨
error: typing 상태와 동일하지만 추가로 오류 메시지가 표시됨
🎈 2. 상태 변경을 촉발하는 요인 파악하기
사람의 입력: 버튼 클릭, 필드입력, 링크 이동
컴퓨터의 입력: 네트워크에서 응답 도착, 시간 초과, 이미지 로딩 등
두 경우 모두 state 변수를 설정해야 UI를 업데이트할 수 있습니다. 개발중인 form의 경우 몇 가지 다른 입력에 따라 state를 변경해야 합니다.
text 입력을 변경 -> text box가 비어있는지 여부에 따라 비어있음 state에서 입력중state로 또는 그 반대로 전환해야 합니다.
- 제출 버튼을 클릭(사람)하면 제출중 state로 전환해야합니다.
- 네트워크 응답 성공(컴퓨터)시 성공 state로 전환해야 합니다.
- 네트워크 요청 실패(컴퓨터)시 일치하는 오류 메세지와 함께 오류 state로 전환해야 합니다.
🎈 3. 메모리의 상태를 useState로 표현하기
단순함이 핵심 !
반드시 필요한 state부터 시작하세요
입력에 대한 answer를 저장하고 가장 마지막에 발생한 error도 저장
const [answer, setAnswer] = useState('');
const [error, setError] = useState(null);
시각적 상태 중 어떤 상태를 표시할지를 나타내는 state 변수가 필요합니다.
const [isEmpty, setIsEmpty] = useState(true);
const [isTyping, setIsTyping] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
const [isError, setIsError] = useState(false);
🎈4. 비필수적인 state변수 제거하기
state 콘텐츠의 중복을 피해 필수적인 것만 추적하고 싶을 것입니다. 목표는 state가 사용자에게 보여주기를 원하는 유효한 UI를 나타내지 않는 경우를 방지하는 것입니다.
- state가 모순을 야기하나요 ? isTyping과 isSubmittng은 동시에 true일 수 없습니다.
불가능한 state를 제거하려면 typing, submitting, success 세가지 값을 하나의 status로 결합하면 됩니다.
- 다른 state 변수에 이미 같은 정보가 있나요 ? isEmpty와 isTyping은 동시에 true가 될 수 없습니다. 이는 각 state 변수로 분리하면 동기화되지 않아 버그가 발생할 위험이 있습니다. 다행히 isEmpty를 제거하고 대신 answer.length === 0으로 확인할 수 있습니다.
- 다른 state 변수를 뒤집으면 동일한 정보를 얻을 수 있나요 ? isError는 error ! == null을 대신 확인할 수 있기때문에 필요하지 않습니다.
3개의 필수 state 변수만 남습니다
const [answer, setAnswer] = useState('');
const [error, setError] = useState(null);
const [status, setStatus] = useState('typing'); // 'typing', 'submitting', or 'success'
🎈 5. 이벤트 핸들러를 연결하여 state 설정하기
import { useState } from 'react';
export default function Form() {
const [answer, setAnswer] = useState('');
const [error, setError] = useState(null);
const [status, setStatus] = useState('typing');
if (status === 'success') {
return <h1>That's right!</h1>
}
async function handleSubmit(e) {
e.preventDefault();
setStatus('submitting');
try {
await submitForm(answer);
setStatus('success');
} catch (err) {
setStatus('typing');
setError(err);
}
}
function handleTextareaChange(e) {
setAnswer(e.target.value);
}
return (
<>
<h2>City quiz</h2>
<p>
In which city is there a billboard that turns air into drinkable water?
</p>
<form onSubmit={handleSubmit}>
<textarea
value={answer}
onChange={handleTextareaChange}
disabled={status === 'submitting'}
/>
<br />
<button disabled={
answer.length === 0 ||
status === 'submitting'
}>
Submit
</button>
{error !== null &&
<p className="Error">
{error.message}
</p>
}
</form>
</>
);
}
function submitForm(answer) {
// Pretend it's hitting the network.
return new Promise((resolve, reject) => {
setTimeout(() => {
let shouldError = answer.toLowerCase() !== 'lima'
if (shouldError) {
reject(new Error('Good guess but a wrong answer. Try again!'));
} else {
resolve();
}
}, 1500);
});
}
'react.js' 카테고리의 다른 글
| react 공식문서 학습 - Responding to Events (0) | 2023.11.29 |
|---|---|
| react 공식문서 학습- Updating Objects in State (1) | 2023.11.28 |
| 한입 크기로 잘라 먹는 리액트 - props (0) | 2023.10.07 |
| 한입 크기로 잘라 먹는 리액트 - State (0) | 2023.10.07 |
| 한입 크기로 잘라 먹는 리액트- jsx (0) | 2023.10.06 |