목차
- recoil을 쓰게 된 이유, 장점 및 단점
-리액트 네이티브 RefreshControl + recoil selector 로 api 재호출하기
recoil을 쓰게 된 이유, 장점 및 단점
기존 프로젝트에서는 놀랍게도 state를 전역으로 관리하지 않았다(놀라움..)
따라서 개발 과정이 매우 번거로웠고, 매 페이지마다 회원 정보를 불러오는 api를 호출해야하는 치명적인 단점이 존재했다.
보다 효율 높은 유지보수를 위해 recoil을 사용하여 전역관리를 하기로 어렵게 결정하였다...! (사용법이 쉽고 요새 많이 쓰는 추세라서..)
무려 70개가 넘는 screen을 다 바꿔야했지만... 미래를 생각해서 진행하기로 했다.
recoil selector를 사용했더니 loading 상태를 별도로 관리하지 않아도 됐고, 모든 페이지에서 같은 api를 부를 필요도 없어졌고, 코드 관리가 쉬워졌다!
그러나.. RefreshControl 로 화면을 강제 refresh할 때 치명적인 문제가 발생하게 됐다.
기존 코드는 하드 코딩으로 refresh 할 때 실행시킬 함수를 넣어주었기 때문에 문제가 전혀 없었다.
그러나 recoil을 사용하기로 한 이상 페이지를 리랜더링하려면 selector를 강제로 리프레쉬해야하는 상황이 발생했다.
recoil selector 에는 강제 refresh 기능이 없다..!
그래서 야매로 selector를 쓰면서 화면 리랜더링을 강제할 수 있는(api를 재호출할 수 있는) 방법을 정리해보고자 한다.
리액트 네이티브 RefreshControl + recoil selector 로 api 재호출하기
(Recoil 기본 세팅은 되어있다는 전제하에 작성하겠음!)
// Sreens/Test.js
import React, { Component, useCallback, useEffect, useState, Suspense } from 'react';
import {
Text,
TouchableOpacity,
View,
FlatList,
ScrollView,
RefreshControl,
Image,
ActivityIndicator,
} from 'react-native';
import { isRecoilValue, useRecoilState, useRecoilValue } from 'recoil';
import { listSelector } from '@recoil/Selectors/test'; //해당 페이지에서 사용할 selector
export default TestComponent = (props)=>{
const [listData, setListData] = useRecoilState(listSelector);
const [isRefreshing, setIsRefresing] = useState(false);
return (
<View
style={{
flex: 1,
}}
>
<ScrollView
refreshControl={
<RefreshControl
refreshing={isRefreshing}
onRefresh={() => {
setListData();
}}
/>
}
>
<TestFlatList listData={listData} />
// 해당 컴포넌트 작성했다고 가정. FlatList 쓰는 컴포넌트
)}
</ScrollView>
</View>
);
}
아래로 당겨서 스크롤될 때마다 강제로 refresh를 해주는 코드이다.
props로 onRefresh라는 함수를 넘겨주어야한다.
그러나 우리는 recoil 로 상태값을 끌어와 쓰기 때문에 api를 호출할 수 없다!
따라서 해당 selector 안에서 사용되는 atom의 값을 강제적으로 변경시켜 selector를 리프레시하는 방법을 사용할 것이다.
// Selectors/test.js
import { selector } from 'recoil';
import { forceTodoUpdateAtom } from 'recoil/Atoms'; // 강제 update(refresh) 용 Atom
import { listAsync } from '@services/API/test'; // Test 페이지에서 사용되는 api이자 refresh할 api
export const listSelector = selector({
key: 'listSelector',
get: async ({ get }) => {
get(forceTodoUpdateAtom); // 이 selector에서는 forceTodoUpdateAtom를 구독한다.
//따라서 이 Atom의 값이 변하게 되면 이 Atom을 참고하는 모든 컴포넌트들의 state가 업데이트되고 리렌더링됨
const res = await listAsync();
return res;
},
set: ({ set }) => set(forceTodoUpdateAtom, (v) => v + 1),
// 여기가 refresh를 위한 Atom의 값을 바꿔주는 작업이다.
// 해당 코드로 인해 refresh가 일어남.
});
Test.js 페이지의 onRefresh 함수가 실행되면 listSelector의 setter를 실행시켜줌으로써 forceUpdateAtom의 값을 변경시킨다.
state값이 변경되기 때문에 forceUpdateAtom를 참조하는 listSeletor 역시 재구동된다.
따라서, 해당 state값을 사용하는 컴포넌트가 리렌더링되는 원리이다.
// recoil/Atoms/index
import { atom } from 'recoil';
export const forceTodoUpdateAtom = atom({
key: 'forceTodoUpdateAtom',
default: 0,
});
새로고침 시 마다 state를 1씩 증가시켜 강제로 리렌더링 한다.
끝.