[recoil] Flux pattern & atom
recoil : 리액트의 상태관리를 위해 사용되는 라이브러리
react의 데이터 흐름은 단방향이다. (부모 컴포넌트 -> 자식 컴포넌트) 이런 패턴을 Flux 패턴에서 유래됐다.
flux 패턴은 mvc 패턴에서의 양방향 데이터 흐름을 방지하기 위해 설계되었다.
mvc패턴
model, view, controller 세가지 요소로 구성된 소프트웨어 디자인패턴
- Model은 데이터와 비즈니스 로직을 처리한다.
앱이 포함해야할 데이터를 정의하고, 데이터의 상태가 변경되면 View에게 알린다. - View는 UI를 나타내는 부분인데 데이터를 시각적으로 표현한다.
사용자의 입력은 이때 View에서 Controller로 전달된다. - Controller는 앱의 사용자 입력에 대한 응답으로 Model이나 View를 업데이트하는 로직을 포함한다.
Model과 View사이의 상호동작을 관리한다.
애플리케이션이 커지면서 정의된 Model과 View가 다양해지고, 데이터 흐름이 많아질 수 있다.
그래서 어떤 데이터가 변경된다면 그 데이터를 사용하는 모든 부분의 코드를 수정해줘야한다.
이런 방식은 오류를 야기하고, Side Effect가 발생할 수 있다.
Flux 패턴
action이 발생하면, dispatcher에서 받아와 해석하고 store에 저장된 데이터에 변경을 가한 뒤에 view에 전달시킨다.
그리고 view에서 사용자 입력이 들어오면 action으로 dispatcher에 전달된다.
- action
이벤트나 사용자 입력이 발생했을 때 그에 대한 action 정보를 담고 있는 객체를 만들어내서 dispatcher에 전달한다 - dispatcher
action 객체 정보를 받아서 어떤 동작을 할지 결정한다.
그리고 등록된 store에 action을 전달한다. - store
데이터나 상태를 관리하는 부분이다.
dispatcher로 받은 action에 따라서 데이터를 업데이트하고, 변경 시 view에게 알린다 - view
UI를 나타낸다.
store의 상태에 따라 업데이트되고, 여기서 발생한 사용자 입력은 action을 발생시킨다.
redux는 Flux 패턴을 적용한 상태관리 라이브러리다.
그러나, Redux는 사용시에 action, dispatcher, reducer, store 등의 구현해야할 코드들이 크다는 단점이 있다.
그에 비해 recoil은 redux에 비해서 여러 방면에서 단순하면서 Flux 패턴에서 영감을 받아 개발된 상태 관리 라이브러리라고 한다.
코드 작성도 간편하고, 상태를 관리하는 방법도 간단해보인다.
그리고 selector를 이용해서 비동기 로직을 쉽게 구현할 수 있다.
atom
컴포넌트가 구독할 수 있는 업데이트가 가능한 상태의 단위
업데이트되면 구독된 컴포넌트는 새로운 값을 반영하여 다시 렌더링된다.
만약 동일한 atom이 여러 컴포넌트에서 사용된다면 모든 컴포넌트는 상태를 공유한다.
atom의 생성은 atom 함수로 이뤄진다.
import { atom } from "recoil"
const fontSizeState = atom({
key : 'fontSizeState',
default : 14,
});
Atom의 key값은 전역적으로 고유해야한다.
default 값은 정적인 값, Promise, RecoilValue(atom, selector)가 될 수는 있다
Promise가 pending(대기 상태)이거나, default selector가 비동기일 수도 있어서, atom 값은 읽을 때 대기 중이거나 오류가 발생할 수 있다.
Atom의 초기값이 Promise인 경우, 해당 Promise가 아직 완료되지 않아서 Atom 값에 접근하면 Promise가 해결될때 까지 기다려야 한다.
그리고 비동기 selector 의 경우, 결과가 반환되기 전까지 Atom 값에 접근하면 기다려야한다.
그래서 대기중인 상태로 처리하거나 오류가 발생할 수 있다는 말인것 같다.
atom에 Promise를 직접 할당할 수는 없다. 그 대신 비동기 함수엔 selector를 사용해야 한다.
즉, 비동기 처리를 할거면 selector를 사용하길 권장한다는 말인 듯함
비동기 함수를 default 값으로 설정할 수는 있지만 권장되지는 않는 것 같다.
recoil에서의 atom은 동기적으로 초기화되어야 한다. (recoil은 기본적으로 상태관리를 동기적으로 처리한다고 한다)
그래서 Recoil에서 비동기적인 초기화를 하려면 useRecoilCallback 훅이나 selector를 이용한 처리를 하는 것이 적절한것 같다
const Atom = atom({
key: 'Atom',
default : null,
})
const loadAtomValue = selector({
key: 'loadAtomValue',
get: async ({ get }) => {
const result = await AsyncFunction();
return result
},
})
const setMyAtomValue = useRecoilCallback(({ set }) => async () => {
const value = await getPromiseFromAsyncFunction();
set(Atom, value);
});
또한 suspense를 활용한다면 비동기 데이터를 처리하고 로딩 상태를 간편히 다룰 수 있다고 한다.
같이 자주 사용되는 hook
- useRecoilState() : reading & writing atom
- useRecoilValue() : only reading
- useSetRecoilState() : only writing
- useResetRecoilState() : reset atom to default value
출처