-
Notifications
You must be signed in to change notification settings - Fork 2
AppStateMgr Concepts
At the time of writing, URSYS WebApps use AppCore Modules to act as a ViewModel that mediates communication between GUI components with other modules.
The AppStateMgr
supports AppCore modules by providing a managed key-value store in with an EventEmitter-like interface.
- When creating an instance of AppStateMgr, you give it a unique "group name" that identifies the collection of related state.
- Each instance stores a React-compatible state object and enforces uniqueness of their keys. It also enforces case consistency internally: group names are forced to
UPPER_SNAKE_CASE
internally, and keys are forced tolower_snake_case
.
Note
If looking through the GEMSTEP project for example uses in AppCore modules, they are using the class-state-manager.ts
version of StateMgr.
The StateMgr class has both private methods and public methods.
-
The public methods do not have an underscore prefix (e.g.
state()
), and are simple access and subscribe methods. The use model is to expose a simple EventEmitter-style interface:subscribe()
,unsubscribe()
,sendState()
, andgetState()
. -
The private methods are preceded with an underscore (e.g.
_setState()
) and are for use only by AppCore module authors. That's because these private methods are used to create complicated state dependencies that we want to hide in the public interface. For example, you might want to intercept thesendState()
call do some special stuff by using the_interceptState()
tap hook.
The StateMgr class is designed to work as a queue.
- a call to
StateMgr.sendState( stateObj )
requests a state change. - StateMgr puts
stateObj
into a queue. If there is a callback defined in thesendState()
call, it's also pushed into a queue. - StateMgr dequeues and merges all pending state into single state object.
- StateMgr notifies all subscribers with the merged state on the next thread tick
- StateMgr invokes callbacks with no parameters (this is to discourage the use of "send then update" patterns that screw up async programming)
- StateMgr invokes side effects that were added on the next thread tick
Note
StateMgr dequeues and processes state objects immediately rather than waiting a tick and allowing more AppCores to pile-on changes. In practice the queue is not very full as it's driven by user interface interactions that happen relatively slowly.
This typically happens at the top of an AppCore module. It initializes a new key-value object named "token-editor" with two properties and their initial values. If there is an asyncronous loaded dependency, you could wrap this code in the LOAD_ASSETS
phase to force it to happen before React initializes.
const STORE = new StateMgr('token-editor');
STORE._initializeState({
script_tokens: [],
script_name: ''
});
These are what you would expect to see used in a class-style React.Component. See Using React with URSYS for more detail.
-
getState()
,getState( propName )
- retrieves the entire state dictionary or a specific property. -
sendState( stateObj )
- Queues a state change request -
subscribe( callbackFunc )
- Receive state changes when they occur -
unsubscribe( callbackFunc )
- Remove state change notification
Additionally, there is the "side effect" hook:
-
queueEffect( effectFunc )
- Queue a function to be executed aftersendState()
has completed notifying subscribers and they have all executed.
These static class methods are provided provide access to state groups by name.
-
StateMgr.GetStateManager( groupName )
- Retrieves the StateMgr instance by its name -
StateMgr.GetStateData( groupName )
- Retrieves the state dictionary object by group name -
StateMgr.NewInstance( groupName )
- Create/return a new instance of StateMgr if you hate thenew
keyword
These methods are used to inspect and manipulate state as it is received. Use only inside the AppCore module that owns/initializes the StateMgr.
-
_interceptState( tapFunc )
- Your chance to filter the state change data before it's actually applied.
In your tap function, you might use these methods to do additional manipulation of state.
-
_setState( stateObj )
- Replaces the entire state object. -
_mergeState( stateObj )
- Shallow merge of stateObj, returning the updated state. -
_insertStateEvent( stateEvent, callback )
- your chance to add another state change action manually
These methods provide object manipulation to ensure that each state object generated is a new one
-
_derefProps( stateObj )
- Given a stateObj, returns new obj with any array properties containing a new array with copied values -
_cloneStateObject( stateObj )
- Returns a shallow clone, with_derefProps()
applied beforehand.
These methods implement the stages of state change in an orderly way, and are likely of no use to AppCore authors.
-
_isValidState( stateObj )
- Returns true ifstateObj
conforms to key naming standards. Does not check for nested objects; it's only top-level keys that are tested. -
_enqueue( actionObj )
- Used bysendState()
to queue the state object and any callback functions. -
_dequeue()
- Process the state queue, merging state and handling notification and side effects.
These utility methods support the _dequeue
operation.
-
_doCallbacks()
- Process the callbacks queued by_dequeue()
immediately -
_notifySubs( stateObj )
- Send a clone of each stateObj to every subscriber. -
_doEffect()
- Invokes all queued effect functions on the next Javascript thread tick, allowing time for them to