📖 새로운 상태관리 라이브러리를 사용해보자
recoil은 react의 상태관리 management중 하나로 상태관리를 전역으로 관리 할 수 있는 라이브러리이다.
이 글을 읽는 독자는 redux를 한번 정도 라도 접해본 사람이 읽으면 도움이 될 것이다.
recoil을 설치한 후 redux의 provider 처럼 RecoilRoot란 걸 import 한 후
root에 렌더링 중인 App 컴포넌트를 RecoilRoot로 감싸준다.
recoil 에는 recoil 만의 state가 존재하는데 그걸 atom 이라고 한다
export const atomState = atom({
key : "작명",
value : 값
})
사용 할 컴포넌트에서 useState 처럼 import atomState from ~ 주소
const [state,setRState] = useRecoilState(atomState) 이렇게 쓰면 된다.
상태 변경하는법: 그냥 저렇게 불러오게 되면 어디서든 setRState() 이렇게 해주면 바뀜
useState 처럼 button onclick={() => setRState(액션)} 이렇게 해주면 됨
setRstate가 작동해서 atomState의 value 값이 바뀜 // 이걸 구독이라고 한다.
이때 setRState가 실행되면서 useRecoilState를 사용하고 있는 곳은 전부 리랜더딩 된다.
단 useRecoilValue를 사용할 때는 컴포넌트 내부에서 해당 값이 변경되지 않는 한 컴포넌트는 리렌더링되지 않는다.
Recoil에서는 useRecoilState라는 hook을 사용하여 인자로 전달한 상태의 값과 setter를 가져올 수 있다.
const SomeComponent = () => {
const [mode, setMode] = useRecoilState(modeState);
return <div>mode: {mode}</div>;
};
만약 상태 값만 필요한 경우, useRecoilValue라는 hook을 사용한다.
const SomeComponent = () => {
const mode = useRecoilValue(modeState);
return <div>mode: {mode}</div>;
};
const Button = () => {
const setMode = useSetRecoilState(modeState);
const toggleMode = () => {
setMode(prevMode => (prevMode === 'light' ? 'dark' : 'light'));
};
return (
<button type="button" onClick={toggleMode}>
저를 클릭하시면 mode가 바뀌어요!
</button>
);
};
이 hook을 사용하면 setter만 가져오게 된다.
recoil은 아톰만으로 비동기 처리 할 수 없다.
그래서 selector이란 함수를 써서 비동기 처리를 한다.
const cookieData = useRecoilValue(getCookieSelector);
// 얘는 그냥 getCookieSelector을 불러오는 역할
export const getCookieSelector = selector({
key: "cookie/get",
// 고유의 키값
get: async ({get}) => {
try {
const response = await fetch('/api/cookies'); // 가상의 API 주소
const data = await response.json();
return data;
} catch (error) {
console.error('Error fetching cookie data:', error);
throw error;
}
},
여기서 return data를 set 부분에
set(아톰명, newValue) 이렇게 넣으면 상태가 업데이트 된다.
set: ({set}, newValue)=> { //useRecoilState를 쓰면 자동 실행
//return된 data.data가 set으로 보내짐 ↓
set(cookieState, newValue)
//cookieState라는 아톰에 newValue로 상태값을 바꿈
}
});
})
get이 먼저 실행되냐 set이 먼저 실행되냐는
useRecoilValue을 썻냐 useRecoilState를 썻냐에 달렸다
useRecoilState는 get&set 둘다 호출하는데 RecoilValue는 get만 호출한다.
동시에 수행되는 것이 아니라, 사용하는 상황에 따라 호출되는 타이밍이 다르다.
useRecoilState를 사용한 컴포넌트가 처음 렌더링될 때 get 함수가 실행되고, 이때 Recoil 상태의 현재 값을 읽어 온다.
useRecoilState를 사용한 컴포넌트에서 Recoil 상태를 업데이트할 때, set 함수가 호출되고,
이때 Recoil의 상태가 업데이트 되며, 해당 Recoil의 상태를 구독하고 있는 컴포넌트가 다시 렌더링된다.
다시 렌더링된 컴포넌트에서 useRecoilState가 사용되면
다시 get 함수가 호출되어 최신 값을 가져오게 된다.
이러한 흐름에서 get과 set은 서로 다른 상황에서 호출되며
초기 렌더링 시 get이 먼저 실행되고, 상태를 업데이트할 때 set이 먼저 실행된다.
그리고 비동기 처리 중 로딩되는 구간엔 suspense와 fallback 코드로 다음과 같이 처리해주면 된다.
<RootRecoil>
<Suspense fallback={<div>Loading...</div>}>
//이렇게 suspense fallback={로딩처리하는 컴포넌트나 div을 써주면됨}
// 그럼 다돌면 밑에 Cookies가 랜더링됨
<Cookies />
</Suspense>
</RootRecoil>
selector는 쉽게 말해서 기존에 선언한 atom을 전/후 처리하여 새로운 값을 리턴하거나
기존 atom의 값을 수정하는 역할을 수행한다. 그리고 참조한 atom의 값이 최신화되면 자동으로
selector의 값도 최신화하므로, 관리하기에도 간편하다.
export const sampleState = atom({
key: "sampleState",
default: 0,
});
export const sampleSelector = selector({
key: "sampleSelector",
get: ({ get }) => get(sampleState) * 2,
set: ({ set }, newValue) => set(sampleState, newValue / 2),
});
가장 기본적인 예제를 한번 만들어봤다. sampleSelector는 sampleState를 참조해
sampleState * 2 라는 값을 리턴하고 있다.
또한 setter를 보면 새롭게 입력된 값을 나누기 2 하여 sampleState에 적용하고 있다.
이처럼 selector를 이용하면 기존에 선언한 atom들을 참조하여 새로운 값을 만들어내고,
새롭게 입력받은 값을 후처리하여 atom의 값까지도 수정하도록 설정할 수 있다.