useStore에는 특별한 메서드가 하나 더 숨겨져있습니다. Provider 메서드는 caro-kann에서 제공하는 특별한 기능으로, 동일한 store를 사용하면서도 독립적인 상태 트리를 구성할 수 있게 해줍니다. 이 기능을 통해 애플리케이션 내에서 같은 store의 서로 다른 인스턴스를 생성하고 관리할 수 있습니다.
Provider는 리액트 컴포넌트처럼 동작하며, store를 props로 받습니다. useStore는 create를 통해 생성된 store에 접근하는 수단이지, store 그 자체는 아닙니다. 따라서 caro-kann은 useStore 대신 store를 직접 리턴하는 createStore 함수를 제공합니다. 이를 통해 특정 컴포넌트 트리에 독립적인 상태를 제공할 수 있습니다.
const useCount = create(0);
const countStore = createStore(0)
function App() {
return (
<div>
{/* 기본 상태를 사용하는 컴포넌트 */}
<Counter />
{/* 독립적인 상태를 가진 Provider */}
<useCount.Provider store={countStore}>
<Counter />
</useCount.Provider>
</div>
);
}
function Counter() {
// 각 Counter는 자신이 속한 Provider의 상태를 사용
const [count, setCount] = useStore();
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(prev => prev + 1)}>Increment</button>
</div>
);
}
나중에 자세히 살펴보겠지만 caro-kann의 middleware는 create와 createStore에 추가적인 기능을 부여합니다. 그리고 Provider는 스토어의 타입이 같다면 어떤 미들웨어를 어떤 순서로 사용하든 문제없이 동작합니다. 심지어 아래 예시처럼 create와 createStore에 서로 다른 미들웨어를 적용해도 정상적으로 작동합니다.
const useCount = create(logger(persist(0, { local: 'count' })));
const countStore = createStore(debounce(devtools(0, 'countStore')))
function App() {
return (
<div>
{/* 기본 상태를 사용하는 컴포넌트 */}
<Counter />
{/* 독립적인 상태를 가진 Provider */}
<useCount.Provider store={countStore}>
<Counter />
</useCount.Provider>
</div>
);
}
function Counter() {
// 각 Counter는 자신이 속한 Provider의 상태를 사용
const [count, setCount] = useStore();
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(prev => prev + 1)}>Increment</button>
</div>
);
}
하지만 여기에는 한 가지 예외가 있습니다. create 함수에 reducer 미들웨어를 사용했다면 createStore 함수에도 동일하게 reducer 미들웨어를 사용해야 하며, 그 반대의 경우도 마찬가지입니다. 이는 reducer 미들웨어가 스토어의 상태를 변경하는 핵심 함수인 setValue의 동작 방식을 수정하기 때문입니다. 따라서 create와 createStore 간에 reducer 미들웨어 사용 여부가 일치하지 않으면, 상태 업데이트 로직에 예상치 못한 문제가 발생할 수 있습니다.
다행히 타입스크립트를 사용하면 caro-kann은 내부적으로 각각의 스토어에 적용된 미들웨어를 자동으로 파악합니다. 만약 reducer 미들웨어 사용에 불일치가 감지될 경우, 다음과 같은 경고 메시지를 통해 개발자에게 알려줍니다.
"Warning: Reducer usage must be consistent. Both should use reducers, or neither should."
이를 통해 개발 과정에서 발생할 수 있는 잠재적인 오류를 미리 방지할 수 있도록 돕습니다.