[React] recoil 라이브러리
recoil로 상태 관리하기
Main 화면
메인 화면은 이렇게 생겼다. 리스트에서 식당을 눌렀을 때 또는 지도에서 마커를 눌렀을 때 같은 식당인지 어떻게 확인할까?
1. props로 값 넘겨주기
Main에서 선택된 식당을 관리하고 List와 Map 컴포넌트에 props로 전달해주면 된다.
2. recoil로 전연 변수 관리하기
이 프로젝트에서는 recoil로 선택된 식당을 전역으로 관리했다. 아래에 그 방법을 설명해두었다.
설치
npm install recoil
RecoilRoot
App.js
import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import BrowserMain from "./pages/Main/BrowserMain";
import { RecoilRoot } from "recoil";
function App() {
  return (
    <RecoilRoot>
      <div className="App">
        <BrowserRouter>
          <div className="browser-main">
            <Routes>
              <Route path="/" element={<BrowserMain />} />
            </Routes>
          </div>
        </BrowserRouter>
      </div>
    </RecoilRoot>
  );
}
export default App;
Atom
- Atom은 상태(state)의 일부를 나타낸다.
- Atoms는 어떤 컴포넌트에서나 읽고 쓸 수 있다.
- atom의 값을 읽는 컴포넌트들은 암묵적으로 atom을 구독한다. 그래서 atom에 어떤 변화가 있으면 그 atom을 구독하는 모든 컴포넌트가 재 렌더링 되는 결과가 발생할 것이다.
atom.js
import {
  RecoilRoot,
  atom,
  selector,
  useRecoilState,
  useRecoilValue,
} from "recoil";
import { recoilPersist } from "recoil-persist";
const { persistAtom } = recoilPersist();
export const selectStore = atom({
  key: "selectStore",
  default: null,
  effects_UNSTABLE: [persistAtom],
});
Selector
- Selector는 파생된 상태(derived state)의 일부를 나타낸다.
- 파생된 상태는 상태의 변화다. 파생된 상태를 어떤 방법으로든 주어진 상태를 수정하는 순수 함수에 전달된 상태의 결과물로 생각할 수 있다.
프로젝트에서는 사용하진 않았지만 리코일의 주요 기능이므로 넣었다. Recoil은 변수만 관리하는게 아니라 함수처럼 사용할 수도 있다.
import { atom, selector } from "recoil";
export const wonState = atom({
  key: "wonState",
  default: 0,
});
export const dollarState = selector({
  key: "dollarState",
  get: ({ get }) => get(wonState) / 1400,
  set: ({ set }, dollar) => set(wonState, dollar * 1400),
});
컴포넌트 적용
CheckStore.js
import React from "react";
import { useRecoilState } from "recoil";
import { selectStore } from "../../../../Atom";
const CheckStore = (props) => {
  const [checkedStore, setcheckedStore] = useRecoilState(selectStore);
  //선택했을 때
  const clickStore = () => {
    setcheckedStore(store.id);
  };
  return (
    <div key={store.id} onClick={clickStore}>
      <!-- 리스트 한개 요소 -->
    </div>
  );
};
export default CheckStore;
Map.js
import { useState, useEffect, useRef } from "react";
import { useRecoilState } from "recoil";
import { selectStore } from "../../../Atom";
function Map() {
  const { naver } = window;
  const mapElement = useRef(null);
  const [store, setStore] = useRecoilState(selectStore);
  // 지도
  useEffect(() => {
    if (!mapElement.current || !naver) return;
    const location = new naver.maps.LatLng(centerLat, centerLong);
    const mapOptions: naver.maps.MapOptions = {
      //생략
    };
    const map = new naver.maps.Map(mapElement.current, mapOptions);
    //식당 위치
    for (let i = 0; i < stores.length; i++) {
      //지오코딩(주소->좌표)
      naver.maps.Service.geocode(
        { query: stores[i].address },
        function (status, response) {
          if (status === naver.maps.Service.Status.ERROR) {
            return alert("문제가 발생했습니다.");
          }
          //마커 찍기
          const otherMarkers = new naver.maps.Marker({
            //생략
          });
          //정보창
          const infowindow = new naver.maps.InfoWindow({
            //생략
          });
          //마커 클릭하면 정보창 뜨게함
          naver.maps.Event.addListener(otherMarkers, "click", function (e) {
            setStore(stores[i].id); //이 부분이 식당 선택
          });
          if (store === stores[i].id) {
            infowindow.open(map, otherMarkers);
          } else {
            infowindow.close();
          }
        }
      );
    }
  }, [store]);
  return <div ref={mapElement} />;
}
export default Map;
import recoil과 Atom.js에서 가져올 값
문법
위 코드 설명
결과보기
리스트에서 ‘소바쿠’를 선택했을 때도 지도에서 해당 식당의 정보창이 열리고,
지도에서 ‘요마시’를 선택했을 때도 리스트에서 해당 식당이 선택되어진 걸 볼 수 있다.
 
      
댓글남기기