Recoil은 redux와 마찬가지로 상태관리를 위한 JS 상태관리 툴이다.
context API 기반으로 구현되어 있으며, 함수형 컴포넌트에서만 사용할 수 있음
Recoil 설치
npm i recoil
// or
yarn add recoil
리코일 state를 사용하는 컴포넌트들은 <RecoilRoot>
를 필요로 한다. <RecoilRoot>
를 사용하는 가장 좋은곳은 root component이다.
import React from 'react';
import {
RecoilRoot,
atom,
selector,
useRecoilState,
useRecoilValue,
} from 'recoil';
function App() {
return (
<RecoilRoot> // 이렇게 감싸주어야한다.
<CharacterCounter />
</RecoilRoot>
);
}
아톰은 상태를 말하며 어떠한 컴포넌트에서 씌여지고 읽혀질 수 있다. 아톰의 밸류를 읽는 컴포넌트들은 암묵적으로 그 아톰에게 참고(subscribe(구독이라고 해도 될거 같다.) )되어지고 있다. 그래서 아톰의 업데이트는 이 아톰을 참고하고 있는 컴포넌트를 리-렌더 시킴
const textState = atom({
key: 'textState', // unique ID
default: '', // default value
});
function CharacterCounter() {
return (
<div>
<TextInput />
<CharacterCount />
</div>
);
}
function TextInput() {
const [text, setText] = useRecoilState(textState);
const onChange = (event) => {
setText(event.target.value);
};
return (
<div>
<input type="text" value={text} onChange={onChange} />
<br />
Echo: {text}
</div>
);
}
아톰 상태값을 필요로 하는 컴포넌트에서 , 원할 때 마다 꺼내쓸 수 있다.
useRecoilState는 useState처럼 사용이 가능한데,
atom선언한 값을 useReocilState로 사용했을 때 전역상태관리가 가능함
Selector는 전역 상태 값을 기반으로 어떤 계산을 통해 파생된 상태(derived state)를 반환하는 순수함수
get
함수만 제공되면 Selector는 읽기만 가능한 RecoilValueReadOnly
객체를 반환. set
함수 또한 제공되며 (optional) Selector는 쓰기 가능한 RecoilState
객체를 반환한다.
get
매개변수를 이용하여 atom이나 다른 selector를 참조할 수 있다
// selectors.js
import { selector } from "recoil";
import { countState } from "./atoms";
export const countNextState = selector({
key: "counterNextState",
get: ({ get }) => {
return get(countState) + 1;
}
});
// CounterInfo.js
import React from "react";
import { useRecoilValue } from "recoil";
import { countNextState } from "../states/selectors";
export const CounterInfo = () => {
const nextCount = useRecoilValue(countNextState);
return <p>the next number is {nextCount}</p>;
};
useRecoilValue()
hook을 사용해서 charCountState 값을 읽을 수 있다
사용방법은 앞서 설명한 useRecoilState()
와 거의 동일하지만 setter함수는 반환하지 않음
그러므로 setter 함수 없이 state를 읽기만 하는 컴포넌트에서 유용함.
useRecoilValueLoadable()
hook을 사용하여 렌더링 중 상태(status)를 확인할 수 있다.
앞서 사용했던 useRecoilValue()
는 Error
를 던지거나 Promise
를 반환하지 않는다.
useRecoilValueLoadable()
는 Loadable
객체를 반환합니다. 이를 이용해 비동기 처리를 할 수 있음.
Lodable 객체
state
: slector의 상태를 반환. hasValue
, hasError
, loading
값 중 하나를 String
으로 반환함.contents
: Loadable이 나타내는 값.hasValue
이면 실제 값
.hasError
이면 throw된 Error
객체.loading
이면 Promise
.이번 프로젝트를 하면서 적용했음.
// atom/News.tsx //
export const getNewsSelector = selector<MainNewsList[]>({
key: "getNewsSelector",
get: async ({ get }) => {
const response = await api.get(`/news`);
return response.data;
},
});
// NewsDetailView.tsx //
const NewsDetailView = () => {
const getNews = useRecoilValueLoadable(getNewsSelector);
const Content = styled.div`
min-height: 100vh;
margin: 0 auto;
color: black;
`;
// const selectNewsLoadable = useRecoilValueLoadable(selectNews);
let LoadablegetNewsList: MainNewsList[] = [];
switch (getNews.state) {
case "hasValue":
LoadablegetNewsList = getNews.contents;
break;
}
return (
<>
<DetailTopBar title="뉴스" />
<Content className="max-w-md bg-stone-100 p-3 pb-8">
<NewsBrandCarouselMenu />
<div className="max-h-screen overflow-auto scrollbar-hide">
<div>
{getNews.state === "loading" ? (
<LoadingBar />
) : (
LoadablegetNewsList.map((item: any, idx: number) => (
<div
className="border-t flex text-left py-3 border-stone-300"
key={idx}
>
<img
src={item.imagePath}
className={`${item.imagePath ? "w-28 mr-2" : "pl-1"}`}
/>
<a href={`${item.url}`}>
<span>{item.subject}</span>
</a>
</div>
))
)}
</div>
</div>
</Content>
</>
);
};
export default NewsDetailView;
로딩 상태일때는 로딩바 컴포넌트로 로딩처리,
로딩 완료되고 값을 성공적으로 받아왔을 때 값을 사용해 컴포넌트 리턴