반응형
recoil 을 사용하면서 가장 마음에 들었던 부분은 비동기 처리가 별도 라이브러리 없이 recoil 만으로 가능하다는 것이다.
recoil 의 selector 를 활용하면 쉽게 비동기 데이터를 가져올 수 있다.
이를 위해 recoil state 를 작성한다. (./src/states/board.js)
import { atom, selector } from 'recoil';
import axios from 'axios';
export const boardSearchState = atom({
key: 'boardSearchState',
default: {
writer: '',
title: '',
content: '',
}
});
export const forceReloadBoardListState = atom({
key: 'forceReloadBoardListState',
default: 0
});
export const boardListSelector = selector({
key: 'boardListSelector',
get: async ({ get }) => {
get(forceReloadBoardListState);
const searchParams = get(boardSearchState);
const { data } = await axios.get('/api/v1/board', {
params: searchParams
});
return data;
},
set: ({ set }) => {
set(forceReloadBoardListState, Math.random());
}
});
- Line 4~11 : board 검색 조건에 대한 atom 객체
- Line 13~16 : board 를 리로드하기 위한 atom 객체
- Line 18~33 : board list 를 가져오기 위한 비동기 selector 객체
- Line 21 : 리로드를 위해 selector 에서 구독
- Line 22 : 검색조건 구독
- Line 24~26 : 비동기 요청
- Line 30~32 : selector 의 set 함수 이지만 내부에서 forceReloadBoardListState 객체의 상태 값을 랜덤 숫자로 변경. forceReloadBoardListState 를 구독하고 있는 selector 를 강제로 리로드 하기 위함.
비동기 selector 를 구독하는 컴포넌트 생성 (./src/components/Board.jsx)
import React, { Suspense } from 'react';
import { useRecoilValue } from 'recoil';
import { boardListSelector } from 'states/board';
const Board = () => {
const boardList = useRecoilValue(boardListSelector);
return (
<Suspense fallback={<div>Loading...</div>}>
<table>
<tbody>
{boardList.map((row, idx) => (
<tr key={idx}>
<td>{row.number}</td>
<td>{row.title}</td>
<td>{row.writer}</td>
<td>{row.createdAt}</td>
</tr>
))}
</tbody>
</table>
</Suspense>
);
}
export default Board;
- Line 6 : selector 를 useRecoilValue 함수를 통해 구독
- Line 9 : 비동기 selector 의 값을 사용하려면 React 에서 제공하는 Suspense 로 묶어야 함. 이 때, fallback 프로퍼티를 통해 아직 값을 가져오지 못했을 때 렌더링 할 컴포넌트를 제공해야 한다.
하지만 나는 Suspense 로 묶으면 화면이 렌더링 될 때, fallback 이 화면을 가려버려서 화면전환이 매끄럽지 못한것같다는 느낌을 받았다.
그래서 나는 recoil 에서 제공하는 useRecoilValueLoadable 과 react 의 useMemo 를 사용해서 렌더링 하였다.
import React, { useMemo } from 'react';
import { useRecoilValueLoadable } from 'recoil';
import { boardListSelector } from 'states/board';
const Board = () => {
const boardList = useRecoilValueLoadable(boardListSelector);
const rows = useMemo(() => {
return boardList?.state === 'hasValue' ? boardList?.contents : []
}, [boardList]);
return (
<table>
<tbody>
{rows.map((row, idx) => (
<tr key={idx}>
<td>{row.number}</td>
<td>{row.title}</td>
<td>{row.writer}</td>
<td>{row.createdAt}</td>
</tr>
))}
</tbody>
</table>
);
}
export default Board;
위 소스는 편의상 작성하였지만 상황에 따라 에러처리나 로딩 처리를 해주는 것이 좋을 것 같다.
※ Loadable 객체는 state 와 contents 프로퍼티를 가지고 있다.
state : atom 이나 selector 의 상태를 보여준다. (hasValue, hasError, loading)
contents : atom 이나 selector 의 값이며, 상태에 따라 다른 값을 갖고 있다.
( hasValue - value , hasError - Error , loading - Promise )
- Line 6 : useRecoilValueLoadable 을 통해 selector를 구독
- Line 8~10 : react 의 useMemo Hook 을 통해 해당 selector 가 값이 있으면 데이터를 리턴하고, 아직 갖고 오지 못했으면 빈 배열([]) 을 리턴
또한, 목록 조회에 리로드 기능이 필요하다면 전에 만들었던 selector 의 set 함수를 그대로 호출하면 된다.
...
import { useRecoilStateLoadable } from 'recoil'
...
const [boardList, reload] = useRecoilStateLoadable(boardListSelector);
const handleClickReload = () => {
reload();
};
...
생략
...
이와 같이 recoil 을 통한 비동기 요청 및 응답은 아주 간단한다.
하루빨리 recoil 이 정식으로 릴리즈 하는 날을 기대한다!
반응형
'React' 카테고리의 다른 글
React userEvent test 코드 작성시 Error: Expected key descriptor but found "" in "" (0) | 2022.05.13 |
---|---|
React + Recoil 초기 셋팅 및 테스트 (0) | 2021.06.11 |
React + Redux 초기 셋팅 및 테스트 (1) | 2021.06.09 |
React + Openlayers 구성 ( By React Context ) (0) | 2021.05.31 |
React(+material-ui) draggable + resizing modal 컴포넌트 (0) | 2021.05.22 |