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

Store Mixins #1537

Closed
btakita opened this issue Jun 12, 2018 · 8 comments
Closed

Store Mixins #1537

btakita opened this issue Jun 12, 2018 · 8 comments

Comments

@btakita
Copy link
Contributor

btakita commented Jun 12, 2018

I just want to post a method I've been using to lazily run mixin logic into a store to get feedback & possibly a better way of doing it. Composable mixins allow more extensibility than subclassing. You can also have dependencies.

export function _mixin__store(name, init) {
	return function mixin__store(store) {
		if (store[name]) return store
		store[name] = {
			store,
			name,
			mixin__store
		}
		init(...arguments)
		return store
	}
}
import __store__company from '__/company/store.mjs'
export const __store__products = _mixin__store('__store__products', store => {
	__store__company(store)
	mixin(store, {
		async reset__products() {
			const { company_id } = this.get()
			const response = await fetch(`/companies/${company_id}/products`)
			const products = await response.json()
			this.set({ products })
		}
	})
	store.on('state', ({ changed }) => {
		if (changed.company_id) {
			store.reset__products()
		}
	})
	store.reset__products()
})
@PaulMaly
Copy link
Contributor

You can just use aggregation for that. Please, check my example here.

@btakita
Copy link
Contributor Author

btakita commented Jun 13, 2018

The mixins do more than just define methods on the class. The mixins also execute code (e.g. store.reset__products(), which loads the products). Observations (.on) can also be instantiated. Same with computed properties (store.compute).

The idea is that mixins allow live addition of behavior onto the store. I'm not aware how an aggregation by itself would enable anything but method definitions. To me it appears that aggregations are a streamlined inheritance api. This type of mixin is a factory, enabling the full store api to be used to mutate the store.

Another thing to consider is that mixins can be called on a store arbitrarily. When subclassing a Store, one is locked into the class definition. As components are added, the Store subclass chain would need to be revised. With mixins, each component need only declare it's mixin dependencies.

Perhaps mixin is an incorrect term. Is there a better name for this abstraction?

@PaulMaly
Copy link
Contributor

You able to do those things with aggregation. Because of we don't have a declarative way to do such things like on in Svelte and also because of nature of Store itself (class-based approach), you able to execute code or make observations in two places: after an instance created and inside of constructor. The second variant looks like a solution for you:

CustomStoreMixin.js

export default class {	
	initializer() {
                this.on('state', ({ changed }) => {
		      if (changed.company_id) {
			    this.resetProducts()
		      }
	        });
		this.resetProducts();
	}
	resetProducts() {
	  ...
	}
}

@btakita
Copy link
Contributor Author

btakita commented Jun 13, 2018

I appreciate this discussion & understand that it's possible to use a class based approach to call code. However, dynamic mixins are more practical when working with large projects with many different combinations of mixins being applied.

There is plenty literature on the Inheritance vs Composition debate and why Composition has a reputation of leading to more cohesive & decoupled code. I wouldn't say stores are "class based". They may be implemented using an ES6 class, however javascript is Prototype-based with "classes" offering syntactic support for the underlying Prototype/Constructor mechanism.

With mixins, one does not need to worry about keeping track of the dependency chain, such as having two mixins invoke the same dependency. It would be problematic to have a parent class inserted twice in the inheritance chain (A -> B -> C -> A -> D). There can even be circular mixin dependencies, though it does not happen often in practice.

Also, behavior can be added after the store is instantiated. This means you have have conditional logic in a component which, after the store is instantiated, loads a child component which invokes a mixin. With the class based approach, you would need to instantiate another store & manage the data mapping, leading to incidental complexity.

@PaulMaly
Copy link
Contributor

PaulMaly commented Jun 13, 2018

Actually, aggregation is a very similar thing as composition and mixins. Perhaps, even equal in most of the cases. It's not an inheritance approach. Here it also calling "mixin-style".

One more about mixins - I already propose them for components #1041, but Svelte community discard that idea for many reasons.

@btakita
Copy link
Contributor Author

btakita commented Jun 13, 2018

It's not an inheritance approach

It's syntactic sugar over an underlying inheritance approach. aggregation(Shape, Colored, ZCoord) creates a base class which in inherited by Rectangle.

class Rectangle extends aggregation(Shape, Colored, ZCoord) {}

I already propose them for components #1041, but Svelte community discard that idea for many reasons.

I appreciate that discussion. Store mixins are 100% javascript & do not require any support by the compiler. The _mixin__store method itself is simple & does not need to be included in svelte core.

I mainly want to bring awareness to this approach. I've been using it for projects with complex dependency graphs & it's been working very well. Maybe there's an underlying need for Svelte patterns to be cataloged?

@PaulBGD
Copy link
Member

PaulBGD commented Jun 14, 2018

I've used a similar method to your mixin method when using store, it makes it a lot easier to develop with stores because of the "cohesive & decoupled code".

@Conduitry
Copy link
Member

In v3, stores are now intended to hold individual pieces of data, and there's no longer a single global thing called The Store. I don't think there are particular mixin-y patterns that we want or need to be encouraging, so I'm going to close this.

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

4 participants