Bunja
Bunja는 가벼운 상태 수명 관리자입니다.
이 라이브러리는 jōtai 아톰을 위한 RAII 래퍼를 제공합니다.
참고 자료:
설치
npm install bunja
Bunja 정의하기
bunja
함수를 사용하여 bunja를 정의할 수 있습니다.
정의된 bunja를 useBunja
훅으로 접근하면 bunja 인스턴스가 생성됩니다.
만약 bunja를 참조하는 렌더 트리의 모든 컴포넌트가 사라지면, bunja 인스턴스는 자동으로 소멸됩니다.
bunja의 생명주기가 시작되고 끝날 때 효과를 트리거하고 싶다면, bunja.effect
필드를 사용할 수 있습니다.
import { bunja } from 'bunja'import { useBunja } from 'bunja/react'const countBunja = bunja([], () => {const countAtom = atom(0)return {countAtom,[bunja.effect]() {console.log('mounted')return () => console.log('unmounted')},}})function MyComponent() {const { countAtom } = useBunja(countBunja)const [count, setCount] = useAtom(countAtom)// 여기에 컴포넌트 로직을 작성하세요}
다른 Bunja에 의존하는 Bunja 정의하기
넓은 범위의 상태와 좁은 범위의 상태를 관리하고 싶다면, (넓은 범위의) bunja에 의존하는 (좁은 범위의) bunja를 만들 수 있습니다.
예를 들어, 페이지 상태를 담당하는 bunja와 모달 상태를 담당하는 bunja를 생각해볼 수 있습니다.
페이지 상태는 모달 상태보다 더 오래 유지되며, 모달 상태는 모달이 열리는 순간부터 닫힐 때까지 존재해야 합니다.
이런 경우 다음과 같이 코드를 작성할 수 있습니다.
const pageBunja = bunja([], () => {const pageStateAtom = atom({})return { pageStateAtom }})const childBunja = bunja([pageBunja], ({ pageStateAtom }) => {const childStateAtom = atom((get) => ({...get(pageStateAtom),child: 'state',}))return { childStateAtom }})const modalBunja = bunja([pageBunja], ({ pageStateAtom }) => {const modalStateAtom = atom((get) => ({...get(pageStateAtom),modal: 'state',}))return { modalStateAtom }})function Page() {const [modalOpen, setModalOpen] = useState(false)return (<><Child />{modalOpen && <Modal />}</>)}function Child() {const { childStateAtom } = useBunja(childBunja)const childState = useAtomValue(childStateAtom)// ...}function Modal() {const { modalStateAtom } = useBunja(modalBunja)const modalState = useAtomValue(modalStateAtom)// ...}
pageBunja
가 직접 useBunja
로 사용되지 않는다는 점에 주목하세요.
childBunja
나 modalBunja
를 useBunja
로 사용할 때, 이들은 pageBunja
에 의존하기 때문에 pageBunja
도 마치 useBunja
로 사용된 것과 같은 효과를 냅니다.
모달이 언마운트되면, 더 이상 useBunja(modalBunja)
를 사용하는 곳이 없으므로 modalBunja
의 인스턴스는 자동으로 제거됩니다.
스코프를 사용한 의존성 주입
로컬 상태 관리를 위해 bunja를 사용할 수 있습니다.
bunja의 의존성으로 스코프를 지정하면, 스코프에 주입된 값에 따라 별도의 bunja 인스턴스가 생성됩니다.
import { bunja, createScope } from 'bunja'const UrlScope = createScope()const fetchBunja = bunja([UrlScope], (url) => {const queryAtom = atomWithQuery((get) => ({queryKey: [url],queryFn: async () => (await fetch(url)).json(),}))return { queryAtom }})
React context를 통해 의존성 주입하기
스코프를 React context에 바인딩하면, 해당 스코프에 의존하는 분자는 해당 React context에서 값을 가져올 수 있습니다.
아래 예제에서는 동일한 fetchBunja
를 참조하는 두 개의 React 인스턴스(<ChildComponent />
)가 있지만, 각각 다른 context 값을 참조하기 때문에 두 개의 별도 분자 인스턴스가 생성됩니다.
import { createContext } from 'react'import { bunja, createScope } from 'bunja'import { bindScope } from 'bunja/react'const UrlContext = createContext('https://example.com/')const UrlScope = createScope()bindScope(UrlScope, UrlContext)const fetchBunja = bunja([UrlScope], (url) => {const queryAtom = atomWithQuery((get) => ({queryKey: [url],queryFn: async () => (await fetch(url)).json(),}))return { queryAtom }})function ParentComponent() {return (<><UrlContext value="https://example.com/foo"><ChildComponent /></UrlContext><UrlContext value="https://example.com/bar"><ChildComponent /></UrlContext></>)}function ChildComponent() {const { queryAtom } = useBunja(fetchBunja)const { data, isPending, isError } = useAtomValue(queryAtom)// 컴포넌트 로직 작성}
createScopeFromContext
함수를 사용하면 스코프 생성과 context 바인딩을 한 번에 처리할 수 있습니다.
import { createContext } from 'react'import { createScopeFromContext } from 'bunja/react'const UrlContext = createContext('https://example.com/')const UrlScope = createScopeFromContext(UrlContext)
스코프에 직접 의존성 주입하기
React 컴포넌트 내부에서 스코프에 주입할 값을 생성하고 바로 사용하고 싶을 수 있습니다. 이럴 때는 별도로 컨텍스트를 감싸지 않고 inject
함수를 사용해 스코프에 값을 주입할 수 있습니다.
import { inject } from 'bunja/react'function MyComponent() {const { queryAtom } = useBunja(fetchBunja,inject([[UrlScope, 'https://example.com/']]),)const { data, isPending, isError } = useAtomValue(queryAtom)// 컴포넌트 로직 작성}