-
Recoil 적용과 에러 해결Frontend 2023. 5. 14. 15:41728x90
프로젝트에서 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-시작하기
728x90'Frontend' 카테고리의 다른 글
CSR vs SSR, Next.js (0) 2023.06.12 Vite로 번들러 마이그레이션 및 에러 해결 (0) 2023.05.24