ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Recoil 적용과 에러 해결
    Frontend 2023. 5. 14. 15:41
    728x90

    프로젝트에서 Recoil을 사용하여 상태관리를 하고자 해서 Recoil에 대해 알아보며 정리한 글이다.

    설치

    yarn add recoil

    RecoilRoot

    리코일 state를 사용하는 컴포넌트들은 를 필요로 한다. 를 사용하는 가장 좋은곳은 root component이다.

    // index.tsx
    import ReactDOM from 'react-dom';
    import App from './App';
    import 'styles/reset.scss';
    import 'styles/global.scss';
    import { RecoilRoot } from 'recoil';
    
    ReactDOM.render(
      <RecoilRoot>
        <App />
      </RecoilRoot>,
      document.getElementById('root'),
    );

    Atom

    Atoms는 상태(state)의 일부를 나타낸다. Atoms는 어떤 컴포넌트에서나 읽고 쓸 수 있다.

    atoms.tsx 파일을 생성하고 Recoil의 atom function 을 사용하여 필요한 데이터를 설정한다.

    atom 은 두 가지를 요구하는데 첫 번째는 key(고유값)로 unique ID 이고 두 번째는 default value(기본값) 이다.

    여러 컴포넌트에서 atom을 구독하고 있다면, 상태가 바뀌면 바뀐 값으로 해당 컴포넌트들이 re-render 된다.

    현재 진행하는 프로젝트에서는 atom에 선택된 노드의 id, isActive, name값을 저장하려고 했으므로 다음과 같이 초기값을 설정했다.

    // state.tsx
    import { atom } from 'recoil';
    import { NodeObject } from 'utils/types';
    
    const initalNodeInfo: NodeObject = { id: 0, isActive: false, name: '' };
    
    export const nodeAtom = atom({
      key: 'nodeAtom',
      default: initalNodeInfo,
    });

    이렇게 정의한 atom은 useRecoilState, useRecoilValue, useSetRecoilState 의 훅으로 사용할 수 있다.

    useRecoilState : useState()와 같이 배열의 첫 번째요소가 상태, 두 번째 요소가 상태를 업데이트 하는 함수 반환, atom 을 읽고 쓰게 하기 위해서 사용

    const [nodeInfo, setNodeInfo] = useRecoilState(nodeAtom);

    useRecoilValue : 상태 값만 필요한 경우에 사용

    const NodeInfo = useRecoilValue(nodeAtom);

    useSetRecoilState : 상태를 업데이트 하는 함수만 필요한 경우 사용, 컴포넌트가 atom 을 쓰게 하기 위해서 사용

    const setNodeInfo = useSetRecoilState(nodeAtom);

    useResetRecoilState : 인자로 받아온 atom의 state를 default값으로 reset

    const resetNodeInfo = useResetRecoilState(nodeAtom);

    이렇게 hook을 이용해서 바꾼 atom 값은 useRecoilValue() 을 사용해서 불러오면 된다.

    const selectedNodeInfo = useRecoilValue(nodeAtom);

    nk.js:41 Uncaught TypeError: Cannot assign to read only property 'vx' of object '#<Object>’

    노드를 클릭하면 useRecoilState를 이용하여 Atom값을 변경한 후 저장하려고 했으나 다음과 같은 오류가 발생했다. 읽기 객체를 변형해서 생긴 오류로 보였다.

    const [nodeInfo, setNodeInfo] = useRecoilState(nodeAtom);
    
    const handleClick = (node: NodeObject) => {
        fgRef.current?.centerAt(node.x, node.y, 1000);
        setModalIsOpen(true);
        setNodeInfo(node);
    };

    위의 코드가 기존 코드이다.

    다음과 같이 필요한 값만 넣는 방법으로 바꿔서 해결했다.

      const [nodeInfo, setNodeInfo] = useRecoilState(nodeAtom);
    
      const handleClick = (node: NodeObject) => {
        fgRef.current?.centerAt(node.x, node.y, 1000);
        setModalIsOpen(true);
        setNodeInfo({ id: node.id, isActive: node.isActive, name: node.name });
      };

    현재 프로젝트에서는 atom만을 사용하여 적용했기 때문에, 밑의 selector예제는 다른 사람들이 작성한 예제를 가져왔다.

    추후 비동기 처리를 selector로 할 경우에 따로 정리를 할 예정이다.


    Selector

    atom 만을 사용해서는 비동기 처리를 할 수 없다. selector를 이용해서 비동기 처리를 한 번에 처리할 수 있다.

    Selectors는 devived state 를 나타냅니다. devived state 란 state 를 입력 받아서 그걸 변형해 반환하는 순수 함수를 거쳐 반환된 값을 말한다.

    • Selector의 타입
    key : selector를 구분할 수 있는 유일한 id, 즉 key 값을 의미합니다.
    get : 에는 derived state 를 return 하는 곳 입니다. 예시 코드에서는 api call을 통해 받아온 data를 return 하게 됩니다. (해당 selector가 갖고 있습니다.)
    export const cookieState = atom({
      key: 'cookieState',
      default: []
    });
    
    export const getCookieSelector = selector({
      key: "cookie/get",
      get: async ({ get }) => {
        try{
          const { data } = await client.get('/cookies');
          return data.data;
        } catch (err) {
            throw err;
        }
      },
      set: ({set}, newValue)=> {
        set(cookieState, newValue)
      }
    });

    다음과 같이 비동기 로직을 selector에선 한 번에 처리 할 수 있습니다. 주의하셔야 할 점은, selector는 순수함수 여야 한다는 것이다.

    순수함수란, 같은 입력이 들어오면, 해당 입력에 대한 출력은 항상 같은 함수라는 뜻을 가지고 있다.

    또한 selector는 read-only 한 RecoilValueReadOnly 객체로서 return 값 만을 가질 수 있고 값을 set 할 순 없는 특징을 가지고 있다.

    selector는 결국 다른 selector나 atom의 값을 get해올 수 있고, get해온 값을 바탕으로 다른 atom의 state를 설정할 수 있는 state를 modify 할 수 있는 역할을 한다.


    REF

    https://hgko1207.github.io/2022/07/25/react-6/

    https://velog.io/@hwanieee/Recoil-시작하기

    https://code-masterjung.tistory.com/128

    https://velog.io/@juno7803/Recoil-Recoil-200-활용하기

    728x90

    'Frontend' 카테고리의 다른 글

    CSR vs SSR, Next.js  (0) 2023.06.12
    Vite로 번들러 마이그레이션 및 에러 해결  (0) 2023.05.24
Designed by Tistory.