createReactiveContext
Creates the hook and related provider for the reactive context. Just like React you can have multiple instances of the same context simply by providing it in different parts of your component tree.
TIP
If a nested component throws an error, the reactive context provided will be disposed and the error is thrown further up the component tree. If you want to recover from nested errors without disposing the context, create error boundaries related to nested components of the ReactiveContextProvider.
import { createReactiveContext } from "@impact-react/[*]";
function CounterStore() {
return {};
}
const useCounterStore = createReactiveContext(CounterStore);
INFO
There are two scenarios where providing a reactive context throws an error in development:
- If you use the
use
hook with a promise in a nested component, but have noSuspense
boundary between the ReactiveContextProvider and the nested component to catch it - If the store is using React hooks
Both scenarios represents something you would normally not do, but might occur as you learn about Impact.
Resolving Reactive Contexts
The core feature of Impact is the ability to resolve reactive contexts. You provide a reactive context using a ReactiveContextProvider
in the component tree and any nested component can now consume it. This is not much different from consuming a regular React context. Where things might start to challenge your technical intuition is when the same hook can be used inside the reactive context to also resolve other reactive contexts up the component tree.
Hopefully this behaviour is an intuitive developer experience, but to scratch your technical itch, let us take on a concrete example by looking at a simplified component tree:
AppStoreProvider
App
AdminRoute
AdminStoreProvider
AdminPage
FeatureStoreProvider
Feature
Each Provider in this tree has its own ReactiveContextContainer instance that is provided down the component tree using React context. This ReactiveContextContainer instance is also passed the parent ReactiveContextContainer from up the component tree using the React context. That means ReactiveContextContainer's represents the same hierarchy of contexts as the React context itself.
So when a reactive context hook is used inside Feature it will useContext
to find the closest ReactiveContextContainer, which comes from FeatureStoreProvider. This ReactiveContextContainer instance has a parent property referencing the ReactiveContextContainer on AdminStoreProvider, which has a parent property referencing the ReactiveContextContainer on AppStoreProvider.
With this in mind we can now explain step by step what happens when the Feature component uses useAppStore
to gain access to a user.
- We first get the ReactiveContextContainer from FeatureStoreProvider using
useContext
- We call
.resolve(AppStore)
on that ReactiveContextContainer - Since AppStore is not handled by that ReactiveContextContainer it will use its parent, the ReactiveContextContainer of AdminStoreProvider, to resolve the context. But it is not there either and again it uses the parent which leads it to the ReactiveContextContainer of AppStoreProvider
- The context is instantiated if necessary and returned
But what if the Feature component uses useFeatureStore
and the FeatureStore itself uses useAppStore
?
- We first get the ReactiveContextContainer from FeatureStoreProvider using
useContext
- We call
.resolve(FeatureStore)
on that ReactiveContextContainer - Since FeatureStore is handled by this ReactiveContextContainer and it has not been instantiated yet, it will be instantiated
- During instantiation the ReactiveContextContainer is set as the currently active container. When
useAppStore
is called in FeatureStore it will use the active ReactiveContextContainer to resolve the AppStore, which basically brings us to point 3. on the previous example
The following deep dive video goes into even more detail on how this is implemented, but hopefully this helped scratch your technical itch and gave a deeper understanding of how stores are resolved through the component tree both from components and other stores.