티스토리 뷰
Recoil
전역상태관리 라이브러리로써 context, mob x, redux,.. etc
가 있다.
Recoil
은 그 중 하나로써 최근에 나온 라이브러리다.
Recoil은 크게 atoms, selectors으로 이루어져있다.
atoms
atoms은 상태의 단위로 업데이트 및 구독이 가능하다. => state로 생각
atom
이 업데이트 되면 이를 구독하는 컴포넌트들이 리랜더링된다.
atom
함수를 사용해 생성한다.
const aState = atom({
key: 'uniqueKey',
default: defaultValue,
})
key
, default
를 가지며 각각 전역적인 고유키, 초기 값을 뜻한다.
useRecoil()
컴포넌트에서 읽고 쓰기 위해서 useRecoilState
를 사용한다. => 상태를 컴포넌트간 공유한다는 점에서 useState
와 차이가 존재한다.
selector
은 writable하지 않기에 이를 사용하지 않는다.
function Button() {
const [fontSize, setFontSize] = useRecoilState(fontSizeState);
return (
<button onClick={() => setFontSize((size) => size + 1)} style={{fontSize}}>
Click to Enlarge
</button>
);
}
//타 컴포넌트에서도 공유가능한 RecoilState
function Text() {
const [fontSize, setFontSize] = useRecoilState(fontSizeState);
return <p style={{fontSize}}>This text will increase in size too.</p>;
}
Selectors
atom, selector를 입력으로 받는 순수 함수
상위의 atoms, selectors
가 업데이트되면 하위의 selector()도 다시 실행된다.
atoms과 마찬가지로 구독할 수 있고 변경되면 컴포넌트들이 리랜더링된다.
"읽기가 가능한", "쓰기가 가능한"으로 구분되고 이는 getter, setter
여부에 따라 결정된다.
공식문서상 "읽기만 가능한 selector"는 getter
만 있는 것을 의미한다.
상태를 기반으로 하는 파생 데이터를 계산하는데 사용됨. -> ?
최소한의 상태만 atoms에 저장하고 다른 파생데이터는 selectors에 명시한 함수를 통해 계산.
Atoms과 동일한 인터페이스를 가져서 서로 대체할 수 있음.
const fontSizeLabelState = selector({
key: 'fontSizeLabelState',
get: ({get}) => {
const fontSize = get(fontSizeState);
const unit = 'px';
return `${fontSize}${unit}`;
},
});
get
을 사용해 atoms, 다른 selectors에 접근 가능하다. 근데 이처럼 다른 곳에 접근하면 종속관계가 생겨서 참조한 것이 업데이트 되면 해당 함수도 다시 실행된다.
순수함수라는 점에서 리덕스의 Reducer와 같은 역할을 하는 듯하다.
-> 순수함수는 외부 상태를 변경하지 않는 함수로써 "변경이란 즉, 수정을 뜻한다." state비교에서 사용되는 알고리즘은 얕은 비교로, 주소값만 비교하여 변경을 감지한다. 외부 상태를 변경하지 않고 새로운 상태를 반환하면서 속도도 보장받고 변경알고리즘이 이를 감시할 수 있도록하기 위해 redux에서 순수함수를 사용한다.
useRecoilValue()
atom
, selectors
값 읽기
const fontSizeLabel = useRecoilValue(fontSizeLabelState);
비동기요청
recoil을 사용한 비동기처리의 방법은 다양하다.
공식문서 비동기쿼리를 참고하자
내가 적용할 방법은 selector
을 사용하는 것이다.
이것도 두가지로 나뉘는데 파라미터의 유무에 따라 갈라진다.
파라미터 없을 때
const currentUserNameQuery = selector({
key: 'CurrentUserName',
get: async ({get}) => {
const response = await myDBQuery({
userID: get(currentUserIDState),
});
return response.name;
},
});
function CurrentUserInfo() {
const userName = useRecoilValue(currentUserNameQuery);
return <div>{userName}</div>;
}
해당 방법을 사용한다면 요청을 하는 컴포넌트를 React.Suspense
로 감싸주고 요청에 대한 에러 핸들링을 위해 ErrorBoundary
또한 감싸주어야한다.
function MyApp() {
return (
<RecoilRoot>
<ErrorBoundary>
<React.Suspense fallback={<div>Loading</div>}>
<CurrentUserInfo />
</React.Suspense>
</ErrorBoundary>
</RecoilRoot>
);
}
파라미터가 있을 때
selectorFamily
를 이용한다.
바운더리, suspense로 감싸는건 동일하다.
const userNameQuery = selectorFamily({
key: 'UserName',
get: (userID) => async () => {
const response = await myDBQuery({userID});
if (response.error) {
throw response.error;
}
return response.name;
},
});
function UserInfo({userID}) {
const userName = useRecoilValue(userNameQuery(userID));
return <div>{userName}</div>;
}
랜더링이전에 받아오는 방법도 존재한다. (Pre-fetching)
또한 기본 atom값을 쿼리로 지정하는 방법도 존재한다.
const currentUserIDState = atom({
key: 'CurrentUserID',
default: selector({
key: 'CurrentUserID/Default',
get: () => myFetchCurrentUserID(),
}),
});
Suspense
를 사용하지 않는 방법도 있다.
- 16.6에 추가된 컴포넌트로, 일부 코드가 로드될 때까지 대기하는 동안 특정 컴포넌트를 지정할 수 있다. ->
spinner
등, - 공식문서
출처
'프로그래밍 > React' 카테고리의 다른 글
React에서 Infinite scroll 구현하기 (IntersectionObserver) (0) | 2021.06.19 |
---|---|
Styled-Components With TS(theme, globalStyle) (0) | 2021.06.12 |
Scroll Restoration (0) | 2021.04.30 |
React에서의 Key의 역할 (0) | 2021.04.20 |
SSR ,CSR (0) | 2021.04.20 |
- Total
- Today
- Yesterday