🔗 레파지토리에서 보기
📌 React Context 생성하기
📖 새로운 React Context 생성하기
💎 store/notification-context.js
import { createContext, useState } from "react";
const NotificationContext = createContext({
notification: null,
showNotification: function () {},
hideNotification: function () {},
});
export function NotificationContextProvider({ children }) {
return (
<NotificationContext.Provider>{children}</NotificationContext.Provider>
);
}
export default NotificationContext;
💎 pages/_app.js
import Head from "next/head";
import Layout from "../components/layout/layout";
import Notification from "../components/ui/notification.js";
import { NotificationContextProvider } from "../store/notification-context.js";
import "../styles/globals.css";
function MyApp({ Component, pageProps }) {
return (
<NotificationContextProvider>
<Layout>
<Head>
<title>Next Events</title>
<meta name="description" content="NextJS Events" />
<meta
name="viewport"
content="initial-scale=1.0, width=device-width"
/>
</Head>
<Component {...pageProps} />
<Notification title="Test" message="This is a test." status="pending" />
</Layout>
</NotificationContextProvider>
);
}
export default MyApp;
📖 콘텍스트 상태 추가하기
💎 store/notification-context.js
import { createContext, useState } from "react";
const NotificationContext = createContext({
notification: null,
showNotification: function (notificationData) {},
hideNotification: function () {},
});
export function NotificationContextProvider({ children }) {
const [activeNotification, setActiveNotification] = useState();
function showNotificationHandler(notificationData) {
setActiveNotification(notificationData);
}
function hideNotificationHandler() {
setActiveNotification(null);
}
const context = {
notification: activeNotification,
showNotification: showNotificationHandler,
hideNotification: hideNotificationHandler,
};
return (
<NotificationContext.Provider value={context}>
{children}
</NotificationContext.Provider>
);
}
export default NotificationContext;
📖 컴포넌트 내에서 콘텍스트 데이터 사용하기
- Layout 자체가 콘텐츠를 감싸는 wrapper이므로 이 안에 Notification을 추가할 것.
import { Fragment, useContext } from "react";
import MainHeader from "./main-header";
import Notification from "../ui/notification";
import NotificationContext from "../../store/notification-context";
function Layout(props) {
const NotificationCtx = useContext(NotificationContext);
const activeNotification = NotificationCtx.notification;
return (
<Fragment>
<MainHeader />
<main>{props.children}</main>
{activeNotification && (
<Notification
title={activeNotification.title}
message={activeNotification.message}
status={activeNotification.status}
/>
)}
</Fragment>
);
}
export default Layout;
📖 예시 : 알림 트리거 & 표시하기
import { useRef, useContext } from "react";
import classes from "./newsletter-registration.module.css";
import NotificationContext from "../../store/notification-context.js";
function NewsletterRegistration() {
const emailInputRef = useRef();
const notificationCtx = useContext(NotificationContext);
function registrationHandler(event) {
event.preventDefault();
const enteredEmail = emailInputRef.current.value;
notificationCtx.showNotification({
title: "구독 신청 중...",
message: "뉴스레터 구독 신청 중",
status: "pending",
});
fetch("api/newsletter", {
method: "POST",
body: JSON.stringify({ email: enteredEmail }),
headers: {
"Content-Type": "application/json",
},
})
.then((response) => {
if (response.ok) {
return response.json();
}
return response.json().then((data) => {
throw new Error(data.message || "문제가 발생했습니다!");
});
})
.then((data) => {
notificationCtx.showNotification({
title: "구독 성공",
message: "뉴스레터 구독 신청을 성공적으로 마쳤습니다!",
status: "success",
});
console.log(data);
emailInputRef.current.value = null;
})
.catch((error) => {
notificationCtx.showNotification({
title: "구독 실패",
message: error.message || "뉴스레터 구독하는데 실패했습니다.",
status: "error",
});
});
}
return (
<section className={classes.newsletter}>
<h2>Sign up to stay updated!</h2>
<form onSubmit={registrationHandler}>
<div className={classes.control}>
<input
type="email"
id="email"
placeholder="Your email"
aria-label="Your email"
ref={emailInputRef}
/>
<button>Register</button>
</div>
</form>
</section>
);
}
export default NewsletterRegistration;
- 뉴스레터에 대한 알림 창(pending, success, error)를 실행할 수 있도록 작성

📖 예시 : 알림 자동으로 제거하기
💎 1. notification을 클릭했을 때 제거하기
import { useContext } from "react";
import classes from "./notification.module.css";
import NotificationContext from "../../store/notification-context.js";
function Notification(props) {
const notificationCtx = useContext(NotificationContext);
const { title, message, status } = props;
let statusClasses = "";
if (status === "success") {
statusClasses = classes.success;
}
if (status === "error") {
statusClasses = classes.error;
}
if (status === "pending") {
statusClasses = classes.pending;
}
const activeClasses = `${classes.notification} ${statusClasses}`;
return (
<div className={activeClasses} onClick={notificationCtx.hideNotification}>
<h2>{title}</h2>
<p>{message}</p>
</div>
);
}
export default Notification;
onClick
을 추가하여 클릭했을 때 notificationCtx.hideNotificationHandler
가 작동하도록 한다.
💎 2. 타이머를 이용하여 자동으로 제거하기
import { createContext, useState, useEffect } from "react";
const NotificationContext = createContext({
notification: null,
showNotification: function (notificationData) {},
hideNotification: function () {},
});
export function NotificationContextProvider({ children }) {
const [activeNotification, setActiveNotification] = useState();
function showNotificationHandler(notificationData) {
setActiveNotification(notificationData);
}
function hideNotificationHandler() {
setActiveNotification(null);
}
useEffect(() => {
if (
activeNotification &&
(activeNotification.status === "success" ||
activeNotification.status === "error")
) {
const timer = setTimeout(() => {
hideNotificationHandler();
}, 3000);
return () => {
clearTimeout(timer);
};
}
}, [activeNotification]);
const context = {
notification: activeNotification,
showNotification: showNotificationHandler,
hideNotification: hideNotificationHandler,
};
return (
<NotificationContext.Provider value={context}>
{children}
</NotificationContext.Provider>
);
}
export default NotificationContext;

📖 과제 : 댓글의 notification 표시하기 + 댓글 창의 로딩 메시지 띄우기
💎 스스로 해결하기
import { useEffect, useState, useContext } from "react";
import CommentList from "./comment-list";
import NewComment from "./new-comment";
import classes from "./comments.module.css";
import NotificationContext from "../../store/notification-context";
function Comments(props) {
const { eventId } = props;
const [comments, setComments] = useState([]);
const [showComments, setShowComments] = useState(false);
const notificationCtx = useContext(NotificationContext);
useEffect(() => {
if (showComments) {
notificationCtx.showNotification({
title: "Loading..",
message: "댓글 불러오는 중 입니다.",
status: "pending",
});
fetch(`/api/comments/${eventId}`)
.then((response) => {
if (response.ok) {
return response.json();
}
return response.json().then((data) => {
throw new Error(data.message || "댓글을 불러오는데 실패했습니다.");
});
})
.then((data) => {
setComments(data.comments);
notificationCtx.showNotification({
title: "Success!",
message: "댓글 불러오는데 성공했습니다.",
status: "success",
});
})
.catch((error) => {
notificationCtx.showNotification({
title: "Error!",
message: error.message || "댓글 불러오는데 실패했습니다.",
status: "error",
});
});
}
}, [showComments]);
function toggleCommentsHandler() {
setShowComments((prevStatus) => !prevStatus);
}
function addCommentHandler(commentData) {
notificationCtx.showNotification({
title: "댓글 등록 중...",
message: "댓글 작성 중",
status: "pending",
});
fetch(`/api/comments/${eventId}`, {
method: "POST",
body: JSON.stringify(commentData),
headers: { "Content-Type": "application/json" },
})
.then((response) => {
if (response.ok) {
return response.json();
}
return response.json().then((data) => {
throw new Error(data.message || "댓글 작성하는데 실패했습니다.");
});
})
.then((data) => {
notificationCtx.showNotification({
title: "댓글 작성 성공",
message: "댓글 작성 성공!",
status: "success",
});
})
.catch((error) => {
notificationCtx.showNotification({
title: "댓글 작성 실패",
message: error.message || "댓글 작성 실패했습니다. 다시 시도해주세요",
status: "error",
});
});
}
return (
<section className={classes.comments}>
<button onClick={toggleCommentsHandler}>
{showComments ? "Hide" : "Show"} Comments
</button>
{showComments && <NewComment onAddComment={addCommentHandler} />}
{showComments && <CommentList items={comments} />}
</section>
);
}
export default Comments;
💎 강사 코드
import { useEffect, useState, useContext } from "react";
import CommentList from "./comment-list";
import NewComment from "./new-comment";
import classes from "./comments.module.css";
import NotificationContext from "../../store/notification-context";
function Comments(props) {
const { eventId } = props;
const [comments, setComments] = useState([]);
const [showComments, setShowComments] = useState(false);
const notificationCtx = useContext(NotificationContext);
const [isFetchingComments, setIsFetchinComments] = useState(false);
useEffect(() => {
if (showComments) {
setIsFetchinComments(true);
fetch(`/api/comments/${eventId}`)
.then((response) => response.json())
.then((data) => {
setComments(data.comments);
setIsFetchinComments(false);
});
}
}, [showComments]);
function toggleCommentsHandler() {
setShowComments((prevStatus) => !prevStatus);
}
function addCommentHandler(commentData) {
notificationCtx.showNotification({
title: "댓글 등록 중...",
message: "댓글 작성 중",
status: "pending",
});
fetch(`/api/comments/${eventId}`, {
method: "POST",
body: JSON.stringify(commentData),
headers: { "Content-Type": "application/json" },
})
.then((response) => {
if (response.ok) {
return response.json();
}
return response.json().then((data) => {
throw new Error(data.message || "댓글 작성하는데 실패했습니다.");
});
})
.then((data) => {
notificationCtx.showNotification({
title: "댓글 작성 성공",
message: "댓글 작성 성공!",
status: "success",
});
})
.catch((error) => {
notificationCtx.showNotification({
title: "댓글 작성 실패",
message: error.message || "댓글 작성 실패했습니다. 다시 시도해주세요",
status: "error",
});
});
}
return (
<section className={classes.comments}>
<button onClick={toggleCommentsHandler}>
{showComments ? "Hide" : "Show"} Comments
</button>
{showComments && <NewComment onAddComment={addCommentHandler} />}
{showComments && !isFetchingComments && <CommentList items={comments} />}
{showComments && isFetchingComments && <p>Loading...</p>}
</section>
);
}
export default Comments;