Develop/React Native

[React] Context 를 이용한 전역 데이터 관리

kimpeupeu 2021. 4. 23. 10:16
- 해당 내용은 React Native를 개발하면서 작성했으나 React에도 똑같이 적용할 수 있는 내용
- useContext example

필자가 React Native를 이용해서 현재 위치를 기반으로 리스트의 각 아이템별로 데이터를 업데이트 해줘야하는 상황이었다.

단순히 각 아이템 마다 위치정보를 구독하고 계산해서 업데이트하려니 상당히 지저분하고 여러개가 구독하고 있는 괴상한 구조가 되었다.

목표는 단일 구독으로 전체 앱에서 위치정보를 공유할 수 있도록 하는 것.

하루종일 헛짓하다가 시도한 방법은 아래와 같다.

1. useLocation이라는 hook을 만들기

-> 코드만 분리될 뿐 실상은 똑같다. 

2. hook을 싱글톤 처럼 쓰기

-> 그냥 실패. 될리가 없었다....

3. Redux로 관리

-> 구독 관리 및 callback 다루기 실패

 

최종적으로 성공한 것은

useContext를 활용한 Provider 만들기.

 

1. LocationContext 만들기

LocationContext를 만들고 위치 정보를 구독해서 값을 업데이트해줄 Component인 LocationProvider를 만든다.

LocationContext의 Provider는 LocationProvider 내에 들어가고 그 내부에 children을 넣어 자식 요소가 다 적용되도록 한다.

또한 useLocationContext를 작성하여 쉽고 사용하며 코드가 헷갈리지 않게 한다. (useContext(~~~) 를 여러번 쓰는걸 싫어한다.)

// LocationContext.tsx
import React, { useContext } from 'react';
import RNLocation, { Location, Subscription } from 'react-native-location';

export interface LocationContextState {
  location: Location | null;
}

const initialState = {
  location: null,
};

export const LocationContext = React.createContext<LocationContextState>({
  location: null,
});

export const useLocationContext = () => useContext(LocationContext);

let unsubscribe: Subscription;

const LocationProvider: React.FC = ({ children }) => {
  const [state, setState] = React.useState<LocationContextState>(initialState);

  React.useEffect(() => {
    RNLocation.configure({
      distanceFilter: 5.0,
    });

    RNLocation.requestPermission({
      ios: 'whenInUse',
      android: {
        detail: 'fine',
        rationale: {
          title: 'Location permission',
          message: 'We use your location to demo the library',
          buttonPositive: 'OK',
          buttonNegative: 'Cancel',
        },
      },
    }).then(granted => {
      if (granted) {
        unsubscribe = RNLocation.subscribeToLocationUpdates(locations => {
          setState({ location: locations[0] });
        });
      }
    });

    return () => {
      unsubscribe && unsubscribe();
    };
  }, []);

  return (
    <LocationContext.Provider value={state}>
      {children}
    </LocationContext.Provider>
  );
};

export default LocationProvider;

2. App.js에서 최상위 부모에게 LocationProvider 적용

// App.js
export default () => (
    <LocationProvider>
      <App />
    </LocationProvider>
);

3. 위치 정보를 사용할 곳에서 useLocationContext 호출

// ShopListItem.tsx

const ShopListItem: React.FC<ShopListItemProps> = ({ item }) => {
  const locationContext = useLocationContext();

  return (
    <Text>
      {locationContext.location?.longitude},{' '}
      {locationContext.location?.latitude}
    </Text>
  );
};

반응형