JiSoo's Devlog
[Udemy React 완벽 가이드] Section 8 본문
JSX 제한 사항 및 해결 방법
- 루트 수준에서 JSX 효소들이 인접해 있으면 오류 발생
- 일반적으로 JSX에서 루트 JSX 요소는 1개여야 한다
- 값을 반환하거나 값을 변수 또는 속성에 저장하려면 그 값은 반드시 JSX 요소 1개여야 한다
- 인접한 요소들을 div나 사용자가 정의한 컴포넌트로 감싸면 된다
- div 사용하는 대신 네이티브 자바스크립트 배열을 사용할 수도 있다 -> 키 지정
- 키 프롭은 우리가 만든 값으로 추가할 수도 있다
- 실제 DOM으로 렌더링 될 때 리액트 컴포넌트가 많이 중첩될 수 있는데 그 모든 컴포넌트들은 여러 이유로 div로 감싸거나 다른 감싸는 컴포넌트가 필요하다
- 너무 많은 html 요소를 렌더링 하면 궁극적으로 애플리케이션이 느려질 것이다
컴포넌트 감싸기(wrapper) 만들기
- 칠드런 프롭은 사용자 정의 컴포넌트에서 여는 태그와 닫는 태그 사이에 넣어준 모든 내용을 담고 있다
Wrapper.js
const Wrapper = (props) => {
return props.children;
};
export default Wrapper;
리액트 조각
- div 삭제하고 빈 태그만 사용해도 되는데 유효하지 않은 JSX가 된다
- 모든 프로젝트에서 유효한 JSX 일 필요는 없다
<React.Fragment>
<AddUser onAddUser={addUserHandler} />
<UsersList users={usersList} />
</React.Fragment>
- import React, {useState, Fragment} from 'react'; 해주면 <Fragment>만 써줘도 된다
리액트 포털 소개
- 기본적으로 모달은 페이지 위에 표시되는 오버레이이기 때문에 당연히 다른 모든 것 위에 있다
- 따라서 이것이 만약 다른 HTML 코드 안에 중첩되어 있다면 기술적으로는 스타일링 덕분에 작동할지 몰라도 좋은 코드는 아니다
- 모달 HTML 내용을 일반적인 곳이 아니라 다른 곳에서 렌더링 하면 되는데 이렇게 하려면 리액트 포털을 사용하면 된다
return (
<React.Fragment>
<MyModal />
<MyInputForm />
</React.Fragment>
);
↓ ↓ ↓ ↓ ↓
<div class="my-modal">
<h2>A Modal Title!</h2>
</div>
<section>
<h2>Some other content ... </h2>
<form>
<label>Username</label>
<input type="text"/>
</form>
</section>
포털 작업하기
- 포털을 사용하려면 컴포넌트를 이동시킬 장소와 컴포넌트에서 그곳에 포털을 가져가야 한다고 알려줄 필요가 있다
- index.html에 div의 id를 지정해 나중에 이 장소를 찾아오는데 사용한다 -> id는 modal-root 혹은 backdrop-root
- backdrop-root 밑에 overlay-root를 쓰면 모든 종류의 오버레이나 모달, 사이드 드로어 등을 가져올 수 있다
- 새로운 컴포넌트를 만드는 데 동일한 파일에 추가한다. 이유는 이 앱에서 Backdrop 컴포넌트는 모달과 함께 사용하기만 할 것이기 때문
- 리액트 DOM은 브라우저에 대한 리액트용 어댑터의 일종이라 할 수 있다
- 포털의 핵심은 렌더링 된 HTML 내용을 다른 곳으로 옮기는 것
- 일반적으로 컴포넌트를 사용하는 곳에서는 createPortal을 사용하여 해당 컴포넌트의 HTML 내용을 다른 곳으로 포털, 즉 이동시킬 수 있다. 다만 렌더링 되는 실제 DOM 안에서만 가능
ErrorModal.js
import React from "react";
import ReactDOM from "react-dom";
import Card from "./Card";
import Button from "./Button";
import classes from "./ErrorModal.module.css";
const Backdrop = (props) => {
return <div className={classes.backdrop} onClick={props.onConfirm} />;
};
const ModalOverlay = (props) => {
return (
<Card className={classes.modal}>
<header className={classes.header}>
<h2>{props.title}</h2>
</header>
<div className={classes.content}>
<p>{props.message}</p>
</div>
<footer className={classes.actions}>
<Button onClick={props.onConfirm}>Okay</Button>
</footer>
</Card>
);
};
const ErrorModal = (props) => {
return (
<React.Fragment>
{ReactDOM.createPortal(
<Backdrop onConfirm={props.onConfirm} />,
document.getElementById("backdrop-root")
)}
{ReactDOM.createPortal(
<ModalOverlay
title={props.title}
message={props.message}
onConfirm={props.onConfirm}
/>,
document.getElementById("overlay-root")
)}
</React.Fragment>
);
};
export default ErrorModal;
"ref"로 작업하기
- 참조를 뜻하는데 다른 DOM 요소에 접근해서 그것들로 작업할 수 있게 해주는 것
- ref를 사용해 연결을 설정할 수 있다
- ref 값은 항상 객체로 항상 current 프롭을 갖고 있다
- current 프롭은 그 ref가 연결된 실제 값을 갖는다
- ref는 코드가 조금 더 적지만 DOM을 조작한다는 예외적인 일을 해야 하고 state는 더 깔끔하지만 코드를 조금 더 많이 쓴다
AddUser.js
import React, { useState, useRef } from "react";
import Card from "../UI/Card";
import Button from "../UI/Button";
import ErrorModal from "../UI/ErrorModal";
import classes from "./AddUser.module.css";
import Wrapper from "../Helpers/Wrapper";
const AddUser = (props) => {
const nameInputRef = useRef();
const ageInputRef = useRef();
const [error, setError] = useState();
const addUserHandler = (event) => {
event.preventDefault();
const enteredName = nameInputRef.current.value;
const enteredUserAge = ageInputRef.current.value;
if (enteredName.trim().length === 0 || enteredUserAge.trim().length === 0) {
setError({
title: "Invalid input",
message: "Please enter a valid name and age (non-empty values).",
});
return;
}
if (+enteredUserAge < 1) {
setError({
title: "Invalid age",
message: "Please enter a valid age (> 0).",
});
return;
}
props.onAddUser(enteredName, enteredUserAge);
nameInputRef.current.value = "";
ageInputRef.current.value = "";
};
const errorHandler = () => {
setError(null);
};
return (
<Wrapper>
{error && (
<ErrorModal
title={error.title}
message={error.message}
onConfirm={errorHandler}
/>
)}
,
<Card className={classes.input}>
<form onSubmit={addUserHandler}>
<label htmlFor="username">Username</label>
<input id="username" type="text" ref={nameInputRef} />
<label htmlFor="age">Age (Years)</label>
<input id="age" type="number" ref={ageInputRef} />
<Button type="submit">Add User</Button>
</form>
</Card>
</Wrapper>
);
};
export default AddUser;
제어되는 컴포넌트와 제어되지 않는 컴포넌트
- 입력 컴포넌트는 제어되지 않는 컴포넌트가 된다.
- input id="username" type="text" ref={nameInputRef} 이것들은 내부 state이기 때문에 이것들 안으로 반영되는 값은 리액트에 의해 제어되지 않는다
- 입력 컴포넌트는 일반적으로 폼 컴포넌트를 말하는데 브라우저에 의해 내부 state를 갖는 경향이 있는데 사용자 입력을 받아 저장하고 반영하는 인풋 요소가 구성되어 있다
- 리액트 앱에서 해당 컴포넌트로 작업할 때 리액트 state를 입력 컴포넌트에 연결하고 싶기에 리액트에서 인풋 컴포넌트로 작업할 때 제어/제어되지 않음에 대해 이야기한다
728x90
'Frontend > React' 카테고리의 다른 글
[Udemy React 완벽 가이드] Section 11 (1) | 2024.01.04 |
---|---|
[Udemy React 완벽 가이드] Section 10 (2) | 2024.01.04 |
[Udemy React 완벽 가이드] Section 7 (1) | 2024.01.04 |
[Udemy React 완벽 가이드] Section 6 (2) | 2024.01.04 |
[Udemy React 완벽 가이드] Section 5 (2) | 2024.01.04 |