Cute Bow Tie Hearts Blinking Pink Pointer

프론트엔드/React-Native(RN)

[Recoil] Recoil로 리액트 네이티브 컴포넌트 강제로 새로고침하기

청포도 에이드 2023. 10. 20. 17:26
728x90

목차

- 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씩 증가시켜 강제로 리렌더링 한다.

 

 

끝.

728x90