delete 버튼을 누르면 모달 창을 띄우고, 모달 창 이외의 영역을 누르면 모달 창을 닫는 이벤트를 만들어보자.
Todo List 내용이 들어가는 Todo 컴포넌트를 생성하고, App.js에 추가한다.
App.js
function App() {
return (
<div className="App">
<h1>To Do List</h1>
<Todo text="React TDL"/>
<Todo text="Upload velog" />
<Todo text="Read Deep Dive" />
<Todo text="Read Clean Code" />
</div>
);
}
Todo.js
function Todo(props) {
return (
<div className="card">
<h2>{props.text}</h2>
<div className="actions">
<button className="btn">Delete</button>
</div>
</div>
);
}
모달창과 배경 영역을 만들고 스타일을 적용한다.
Modal.js
function Modal(props) {
return (
<div className="modal">
<p>Are you sure?</p>
<button className="btn btn--alt">Cancel</button>
<button className="btn">Confirm</button>
</div>
);
}
Backdrop.js
function Backdrop(props) {
return <div className="backdrop" />;
}
App.js
function App() {
return (
<div className="App">
<h1>To Do List</h1>
<Todo text="React TDL"/>
<Todo text="Upload velog" />
<Todo text="Read Deep Dive" />
<Todo text="Read Clean Code" />
<Modal /> // Modal 컴포넌트 추가
<Backdrop /> // Backdrop 컴포넌트 추가
</div>
);
}
App.js에 Modal과 Backdrop 컴포넌트를 추가한 화면이다.
delete 버튼을 눌렀을 때만 모달 창이 뜨게 만들고 싶다.
함수형 컴포넌트에서 이벤트를 처리하는 방법은 두 가지이다.
useEffect
에 이벤트 리스너를 위치시킨다.onClick
속성을 사용한다.두 번째 방법을 사용해서 이벤트를 처리해보자.
(첫 번째 방법은 useEffect
만 정리할 때 추가할 예정이다.)
Todo.js
function deleteHandler() {
console.log("Clicked");
}
<button className="btn" onClick={deleteHandler}>Delete</button>
onClick
속성에 문자열이 아닌 함수 이름을 전달한다.
onClick="deleteHandler()" ❌
onClick={deleteHandler()} ❌
// 렌더링되는 시점에서 함수가 호출되어버린다.
onClick={deleteHandler} ⭕️
클릭 후 deleteHandler()
함수가 실행된 것을 확인할 수 있다.
클릭 이벤트를 감지하여 모달과 배경 창을 실행시켜보자.
useState
는 리액트의 Hooks 중 하나이다.
컴포넌트에서 보여줘야 하는 내용이 사용자 인터랙션에 따라 바뀌어야 할 때 사용한다.
리액트 16.8 이전 버전에서는 함수형 컴포넌트에서는 상태를 관리할 수 없었지만, 리액트 16.8 에서 Hooks 라는 기능이 도입되면서 함수형 컴포넌트에서도 상태를 관리할 수 있게 되었다.
Todo.js
import { useState } from './react';
// useState 불러오기
export default function Todo(props) {
const [modalIsOpen, setModalState] = useState(false);
// 비구조화 할당
useState
를 사용 할 때에는 상태의 기본값을 파라미터로 넣어서 호출해준다. 반환 값은 배열이며, 첫번째 원소는 현재 상태, 두번째 원소는 Setter 함수다.
Setter 함수는 파라미터로 전달 받은 값을 최신 상태로 설정한다.
function deleteHandler() {
setModalState(true);
// modalIsOpen을 true로 변경.
}
function closeModalHandler() {
setModalState(false);
// modalIsOpen을 false로 변경.
}
return (...)
이제 delete 버튼을 클릭했을 때 modalIsOpen
이 true로 설정되어 Modal 컴포넌트를 열어야 한다.
{modalIsOpen ? <Modal />}
{modalIsOpen ? <Backdrop />}
이렇게 Modal과 Backdrop 컴포넌트를 렌더링할 수 있지만
{modalIsOpen && <Modal />}
{modalIsOpen && <Backdrop />}
&& 연산자를 사용하여 간단하게 작성할 수 있다.
App.js에서 추가했던 Modal과 Backdrop 컴포넌트를 지우고
Todo.js
import { useState } from './react';
export default function Todo(props) {
const [modalIsOpen, setModalState] = useState(false);
function deleteHandler() {
setModalState(true);
}
function closeModalHandler() {
setModalState(false);
}
return (
<div className="card">
<h2>{props.text}</h2>
<div className="actions">
<button className="btn" onClick={deleteHandler}>Delete</button>
</div>
{modalIsOpen && <Modal />}
{modalIsOpen && <Backdrop />}
</div>
);
}
Todo.js
{modalIsOpen && <Modal onCancel = {closeModalHandler} />}
{modalIsOpen && <Backdrop onCancel = {closeModalHandler}/>}
Modal.js
export default function Modal(props) {
function cancelHandler() {
props.onCancel();
}
function confirmHandler() {
props.onConfirm();
}
return (
<div className="modal">
<p>Are you sure?</p>
<button className="btn btn--alt" onClick={cancelHandler}>Cancel</button>
<button className="btn" onClick={confirmHandler()}>Confirm</button>
</div>
);
}
Backdrop.js
export default function Backdrop(props) {
return <div className="backdrop" onClick={props.onCancel}/>;
}