GitHub package.json versionTypeScriptNPM
GitHub package.json versionTypeScriptNPM

External Access

Normally, the context must be rendered within the component tree in order to make its store accessible - any attempt to the contrary leads to a UsageError.
However, once rendered, its store becomes accessible both within its context component tree and externally.

How do I access the store externally?

This is done by obtaining a reference to the Provider component. Once obtained, its current property (i.e. which holds the referenced store object) can be passed around to other parts of the application.
When the reference is no longer needed, be sure to unsubscribe all observers attached through this store reference during this phase.
For external access to the context, 4 store methods have been exposed. Namely:
  1. store.getState(): Provides a static snapshot of the current state.
  2. store.resetState(): Please see descriptions in the store page.
  3. store.setState(): Please see descriptions in the store page.
  4. store.subscribe(...): Provides an API for manual subscription to the state. E.g.
    store.subscribe( // returning an unsubsciber function
        ( changes: Changes<State> ) => void
    ) => VoidFunction
    Provides an API for manual subscription to the state.

Let's see some code!

provider-demo.js
Making our Provider accessible to the parent component.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import React, { forwardRef } from 'react'; import ObservableContext from './context'; // using example from the "Getting Started Page" import Ui from './ui'; // using example from the "Getting Started Page" const initState = {a: { b: { x: { y: { z: [ 2022 ] } } } }}; const ProviderDemo = forwardRef(( props, ref ) => { // signify to parent the exit of the storeRef Provider on component unmount. useEffect(() => () => setTimeout( () => props?.onDismount(), 0 ), []); return ( <ObservableContext.Provider ref={ ref } value={ initState }> <Ui /> </ObservableContext.Provider> ); }; ProviderDemo.displayName = 'ProviderDemo'; export default ProviderDemo;
app.js
Grabbing a ref to the Provider component and sharing it with a class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import React, { useCallback, useEffect, useRef, useState } from 'react'; import ProviderDemo from './provider-demo'; import StoreMonitor from './debug-monitor'; const App = () => { const storeRef = useRef(); const [ refChangeCount, setRefChangeCount ] = useState( 0 ); const updateMonitor = useCallback(() => setRefChangeCount( c => c + 1 ), []); const [ monitor ] = useState(() => new StoreMonitor( d => console.log( d ) )); useEffect(() => { monitor.source = storeRef.current; return () => monitor.cleanup(); }, [ refChangeCount ]); return ( <ProviderDemo onDismount={ updateMonitor } ref={ storeRef } /> ); } export default App;
debug-monitor.js
Using a simple class instance to montor and report changes in the store in realtime.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Monitor { #onEvent; #store; #unsub; constructor( onEvent, store ) { this.onEvent = onEvent; this.source = store; } set onEvent( handler ) { this.#onEvent = handler } get source() { return this.#store } set source( store ) { if( store === this.#store ) { return } this.cleanup(); if( !store ) { return } this.#store = store; this.#onEvent( this.#store.getState() ); this.#unsub = store.subscribe( () => this.#onEvent( this.#store.getState() ) ); } cleanup() { this.#unsub?.(); this.#store = null; } } export default Monitor;
Pro Tips
Store references are simply ReactJS references to the Eagle Eye Provider component. Therefore, after unmounting the Provider component, the storeRef.current becomes empty. So therefore:
if the need exists to maintain the store beyond the life of the Provider, then keep a reference to the state snapshot returned by the last valid storeRef.current.getState() call.
be sure to unsubscribe all external subscribers attached to the Provider's store reference, at or prior to the unmount phase of the Provider component.
State references are always snapshots of the state at the time of access. In essence, the state returned by storeRef.current.getState() are not affected by subsequent updates to the store's state. Any updates to this acquired state never affects the context's state. So therefore, the 4 considerations:
use only the storeRef.current.setState(...) to update the context internal store.
storeRef.current.getState() must be used to obtain the current state value.
use your storeRef.current.subscribe(...) to manually subscribe to state changes and refresh your current state value in realtime.
use the unsubscriber returned by your storeRef's subscribe(...) to unsubscribe from the store when needed.