https://recoiljs.org/ko/docs/introduction/getting-started/
Recoil ์์ํ๊ธฐ | Recoil
React ์ ํ๋ฆฌ์ผ์ด์
์์ฑํ๊ธฐ
recoiljs.org
์ ์ญ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ Recoil
์๋ฅผ๋ค์ด header์์ ๋ฐ๋ ๊ฒ์์ด ๋ฐ์ดํฐ๋ฅผ
๋ค๋ฅธ ์ปดํฌ๋ํธ๋ก ์ ๋ฌํ๊ณ ์ ํ ๋
๋จ๋ฐํฅ์ผ๋ก ์ ๋ฌํด์ผํด์ ๊ณ์ props๋ก ํ๊ณ ๋๊ฒจ์ค์ผํ๋ ๋จ์ ์ด ์๋ค.
ํ์ง๋ง ์ ์ญ์ผ๋ก ์ํ๋ฅผ ๊ด๋ฆฌํ๋ฉด ์ด ์์
์ด ๋จ์ํ๋ผ์
๋ชจ๋ ์ปดํฌ๋ํธ์ ๋ฐ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ณต์ ํ ์ ์์
ํ์ง๋ง Recoil์ ๊ธฐ๋ณธ์ ์ผ๋ก client ๋ผ์ด๋ธ๋ฌ๋ฆฌ์
๋ณ๋์ ํ๋ก๋ฐ์ด๋๋ฅผ ์ ์ ํด์ฃผ๊ณ ์ ์ฉํ๋ค.
npm install recoil
Atom: Recoil์์ atom์ ์ํ์ ๋จ์์
๋๋ค. atom์ ์ฐ๋ฆฌ๊ฐ ์ฝ๊ณ ์ธ ์ ์๋ ์ํ์ ์กฐ๊ฐ์ผ๋ก ์๊ฐํ ์ ์์ต๋๋ค. atom์ ์์ฑํ๋ ค๋ฉด, atom ํจ์๋ฅผ ์ฌ์ฉํ๊ณ ํ์ํ key์ default ๊ฐ์ ์ ๊ณตํด์ผ ํฉ๋๋ค.
"use client";
import { RecoilRoot } from "recoil";
export default function RecoilProvider({ children }: React.PropsWithChildren) {
return <RecoilRoot>{children}</RecoilRoot>;
}
๊ทธ๋ฆฌ๊ณ ๋ ์ด์์์ ์ ์ฉํ๋ค.
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import { ThemeProvider } from "config/material-tailwind-theme-provider";
import ReactQueryClientProvider from "config/ReactQueryClientProvider";
import Header from "components/header";
import Footer from "components/footer";
import RecoilProvider from "config/RecoilProvider";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
{/* @ts-ignore */}
<head>
<link
rel="stylesheet"
// ํฐํธ์ด์ธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋์ํ ์ ์๊ฒ๋ ์ถ๊ฐํ๊ฑฐ์
integrity="sha512-MV7K8+y+gLIBoVD59lQIYicR65iaqukzvf/nwasF0nqhPay5w/9lJmVM2hMDcnK1OnMGCdVK+iQrJ7lzPJQd1w=="
crossOrigin="anonymous"
referrerPolicy="no-referrer"
/>
</head>
<body className={inter.className}>
<RecoilProvider>
<ReactQueryClientProvider>
<ThemeProvider>
<Header />
{children}
<Footer />
</ThemeProvider>
</ReactQueryClientProvider>
</RecoilProvider>
</body>
</html>
);
}
.
import { atom } from "recoil";
export const searchState = atom({
key: "searchState",
default: "",
});
๊ทธ ๋ค Header ์ปดํฌ๋ํธ์์
useRecoilState๋ฅผ ์ฌ์ฉํด์ (useState์ ๋์ผํ ๊ธฐ๋ฅ)
searchState์ ๊ฒ์์ด๋ฅผ ์ ์ฅํ๋ค.
input์ onchange ์ด๋ฒคํธ๊ฐ ์ผ์ด๋๋ฉด ๊ทธ ๊ฒ์์ด๋ฅผ ์ ์ญ์ผ๋ก ์ํ ๊ด๋ฆฌํจ
"use client";
import { useRecoilState } from "recoil";
import Logo from "./logo";
import { searchState } from "utils/recoil/atoms";
export default function Header() {
const [search, setSearch] = useRecoilState(searchState);
return (
<header className="fixed top-0 left-0 right-0 px-4 py-2 bg-gray-900 flex items-center justify-between z-50">
<nav className="flex gap-4">
<Logo />
<ul className="flex gap-2 text-white">
<li>Movies</li>
<li>Drams</li>
</ul>
</nav>
<div className="flex gap-2 w-full max-w-72 items-center border border-white rounded-md p-2 bg-transparent text-white">
<i className="fas fa-search" />
<input
className="bg-transparent "
placeholder="Search Movies"
onChange={(e) => setSearch(e.target.value)}
/>
</div>
</header>
);
}
๊ทธ๋ฆฌ๊ณ
useRecoilValue๋ฅผ ์ฌ์ฉํด์ ( ์ฝ๊ธฐ๋ง ๊ฐ๋ฅ ) ๊ฒ์์ด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์
useQuery ์ searchMovies ํจ์์ ์ ๋ฌํด์ฃผ๋ฉด๋จ
"use client";
import { useQuery } from "@tanstack/react-query";
import MovieCard from "./movie-card";
import { searchMovies } from "actions/movieActions";
import { Spinner } from "@material-tailwind/react";
import { useRecoilValue } from "recoil";
import { searchState } from "utils/recoil/atoms";
export default function MovieCardList() {
const search = useRecoilValue(searchState);
const getAllMoviesQuery = useQuery({
queryKey: ["movie", search],
queryFn: () => searchMovies(search),
});
return (
<div className="grid gap-1 grid-cols-3 md:grid-cols-4 w-full h-full">
{getAllMoviesQuery.isLoading && <Spinner />}
{getAllMoviesQuery.data &&
getAllMoviesQuery.data.map((movie) => (
<MovieCard key={movie.id} movie={movie} />
))}
</div>
);
}