Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get rid of global objects and side effects #181

Open
smikula opened this issue Jun 7, 2024 · 0 comments
Open

Get rid of global objects and side effects #181

smikula opened this issue Jun 7, 2024 · 0 comments

Comments

@smikula
Copy link
Contributor

smikula commented Jun 7, 2024

This is a proposal for a breaking change to Satchel — this would be Satchel v5. The goal here is to get rid of the two great evils in the existing implementation: global objects and side effects.

Motivation

Today Satchel's internal state — including stores, subscriptions, middleware, etc. — is stored in a global object hanging off the window. Mutators and orchestrators are registered by applying the mutator and orchestrator "decorators" to the handlers. (These APIs aren't really decorators — typically a decorator would just wrap the function in a higher order function — because they aren't wrapping the function but rather registering the function as a callback in the Satchel dispatcher.) This makes for very convenient coding patterns, but it presents some problems:

  • If there are multiple instances of Satchel on a page — for instance if some imported library or component also uses Satchel — then there may be conflicts within the store or configuration.
  • Because mutators and orchestrators are registered as side effects (the registration happens as part of code that runs at module scope) tools like Webpack have trouble applying optimizations like tree shaking to Satchel projects.

Proposal

Create satchel instances with createSatchel

Instead of assuming there's a global store available, consumers will create an instance of a satchel. There can be multiple satchels, configured separately and completely isolated in terms of data and subscribers — but in practice a codebase should only ever create one.

import { createSatchel } from 'satcheljs';

const satchel = createSatchel(options);

The options would include the middleware, replacing the applyMiddleware API. (I don't think there are any other options at this point.)

Explicitly register subscribers

Subscribers (mutators and orchestrators) must be explicitly registered on the satchel. Now mutator and orchestrator will be true decorators — they'll just wrap the handler in an object to be registered separately.

// myMutator.ts
import { mutator } from 'satcheljs';
import { someAction } from './someAction';

export const myMutator = mutator(someAction, message => {
	// Mutate stuff here
});
// initializeMutators.ts
import { mySatchel } from './mySatchel';
import { myMutator } from './myMutator';

export function initializeMutators() {
	mySatchel.register(myMutator);
}

The mutator and orchestrator APIs will simply return an object with some properties. (If we care to keep the properties private we might encapsulate them in a closure and return that.) The properties will include:

  • The action to subscribe to
  • The callback
  • Whether it is a mutator or orchestrator (or, alternately, we could have separate registerMutator and registerOrchestrator APIs on the satchel, but I think just register is cleaner. We could also call it subscribe — thoughts?)
  • A flag indicating whether it has already been registered. Registering the same subscriber subsequent times is no-op. (We don't want the same subscriber get registered multiple times.)

Other APIs

Now that satchel is instanced, various other APIs will need to be changed:

  • mutatorAction will need to take in the satchel as an agument so that it knows where to register the mutator
  • dispatch will become a method on the satchel
  • action will become a method on a satchel so that it knows where to dispatch the action
  • getRootStore will become a method on the satchel

Add a satchel.hasSubscribers(action) API

This will allow consumers to check if there are any subscribers for an action. While not strictly necessary, this will be useful for middleware that wants to know if the dispatched action is handled by anything.

@James-Reilly James-Reilly mentioned this issue Jun 24, 2024
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant