Skip to main content

Sizing and Scoping a Store

First, let's look at this example:

import { Store } from '@geajs/core'

class HugeApplicationStore extends Store {
// Used by Foo Component
bar = 0
baz = 'Hello World!'

// Used by Qux Component
quux: 'me' | 'us' = 'me'

// Used by Corge Component
grault: number[] = []
}

This approach to state management is poor coding practice. Bundling every piece of unrelated state — used by entirely different, unrelated parts of the application — into a single massive Store breaks loose coupling: any change to Foo's state now lives in the same class as Qux's and Corge's, so anyone touching HugeApplicationStore has to understand the whole thing, and testing one concern in isolation means mocking or stubbing out state that has nothing to do with it. Development velocity and maintainability suffer as the store grows.

However, splitting Stores too finely is not advisable either — it becomes difficult to know where a given piece of state lives, and keeping fragmented stores in sync with each other introduces its own coordination overhead. Both extremes are patterns to avoid. So, what is the ideal level of granularity?

Properties of StatesRecommended ScopeDelivery Method
Internal state of a single component (e.g., open/closed flag)Local Variables / Properties of thisNot needed
Shared only within a single component treeLocal Store Generated at the Root of the TreeVia props or via Context
Shared Across the Entire App and Multiple Separate TreesGlobal Store (Module-Level Singleton)Import and reference directly
Rule of thumb

When in doubt, start local. Promote state to a wider scope only once more than one place genuinely needs it — pulling state up later is straightforward, but untangling an over-shared store later is not.