From 0a7bf8ac10550cf87c2b6f06d08948f3d3f1fdcc Mon Sep 17 00:00:00 2001 From: Ajai Shankar <328008+ajaishankar@users.noreply.github.com> Date: Tue, 17 Oct 2023 07:39:54 -0500 Subject: [PATCH] docs(v1.0.0): computed to getter --- README.md | 44 +++++++++++++---------------- docs/cart-controller.js | 43 ++++++++++++++-------------- docs/cart-item-controller.js | 11 ++++---- docs/index.html | 5 ++-- docs/lib/stimulus-reactive.js | 53 ----------------------------------- 5 files changed, 48 insertions(+), 108 deletions(-) diff --git a/README.md b/README.md index 510af23..e67d6b5 100644 --- a/README.md +++ b/README.md @@ -37,13 +37,13 @@ class CartItemController extends Controller { useStimulusReactive(identifier, application); } + get total() { + return this.priceValue * this.quantityValue; + } + connect() { - // item total will be updated when price or quantity changes - this.effect(() => { - this.totalTarget.textContent = ( - this.priceValue * this.quantityValue - ).toString(); - }); + // displayed total will be updated when price or quantity changes + this.effect(() => (this.totalTarget.textContent = this.total.toString())); } } @@ -55,23 +55,20 @@ class CartController extends Controller { useStimulusReactive(identifier, application); } + get total() { + return this.cartItemOutlets.reduce((total, item) => total + item.total, 0); + } + connect() { - // total will be updated when items get added or removed - // or when an item's price or quantity changes - const total = this.computed(() => - this.cartItemOutlets.reduce( - (total, item) => total + item.priceValue * item.quantityValue, - 0 - ) - ); - - // checkout button will be enabled only when balance is due - this.effect(() => (this.checkoutTarget.disabled = total.value <= 0)); - - // text content is kept in sync with cart total - this.effect( - () => (this.cartTotalTarget.textContent = total.value.toString()) - ); + this.effect(() => { + // text content is kept in sync with cart total + this.cartTotalTarget.textContent = total.toString() + // checkout button is enabled only when balance is due + this.checkoutTarget.disabled = total == 0 + }); + + // another effect for some other dependency + this.effect(() => ...); } } ``` @@ -81,10 +78,9 @@ class CartController extends Controller { State lives in a controller's values and connected outlets. 1. Call `useStimulusReactive` in the static `afterLoad` method -2. In the `connect` lifecycle method specify the controller logic using `effect` +2. In the `connect` lifecycle method specify controller behavior using `effect`s 3. Effects will run whenever any dependency changes This will be familiar to those coming from other frameworks like React, Vue etc. -4. Use `computed` (as needed) for any optimizations That's pretty much it! diff --git a/docs/cart-controller.js b/docs/cart-controller.js index 881089b..fcd95f2 100644 --- a/docs/cart-controller.js +++ b/docs/cart-controller.js @@ -6,6 +6,7 @@ export default class extends Controller { shipping: Number, taxRate: Number, }; + static targets = [ "checkout", "subtotal", @@ -20,34 +21,32 @@ export default class extends Controller { useStimulusReactive(identifier, application); } - connect() { - const subtotal = this.computed(() => - this.cartItemOutlets.reduce( - (total, item) => total + item.priceValue * item.quantityValue, - 0 - ) - ); + get subtotal() { + return this.cartItemOutlets.reduce((total, item) => total + item.total, 0); + } - const tax = this.computed(() => (subtotal.value * this.taxRateValue) / 100); + get tax() { + return (this.subtotal * this.taxRateValue) / 100; + } - const shipping = this.computed(() => - subtotal.value ? this.shippingValue : 0 - ); + get shipping() { + return this.subtotal ? this.shippingValue : 0; + } - const total = this.computed( - () => subtotal.value + tax.value + shipping.value - ); + get total() { + return this.subtotal + this.tax + this.shipping; + } + connect() { this.effect(() => { - this.subtotalTarget.textContent = subtotal.value.toFixed(2); - this.shippingTarget.textContent = shipping.value.toFixed(2); - this.taxTarget.textContent = tax.value.toFixed(2); - this.totalTarget.textContent = total.value.toFixed(2); - }); + this.subtotalTarget.textContent = this.subtotal.toFixed(2); + this.shippingTarget.textContent = this.shipping.toFixed(2); + this.taxTarget.textContent = this.tax.toFixed(2); + this.totalTarget.textContent = this.total.toFixed(2); - this.effect(() => { - this.checkoutTarget.disabled = total.value <= 0; - if (total.value > 0) { + this.checkoutTarget.disabled = this.total == 0; + + if (this.total > 0) { this.noItemsMessageTarget.classList.add("hidden"); } else { this.noItemsMessageTarget.classList.remove("hidden"); diff --git a/docs/cart-item-controller.js b/docs/cart-item-controller.js index 8d82440..bdfe3a3 100644 --- a/docs/cart-item-controller.js +++ b/docs/cart-item-controller.js @@ -12,13 +12,12 @@ export default class extends Controller { useStimulusReactive(identifier, application); } + get total() { + return this.priceValue * this.quantityValue; + } + connect() { - this.effect( - () => - (this.totalTarget.textContent = ( - this.priceValue * this.quantityValue - ).toFixed(2)) - ); + this.effect(() => (this.totalTarget.textContent = this.total)); } changeQuantity(e) { diff --git a/docs/index.html b/docs/index.html index 758f501..586d224 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,6 +1,5 @@ - + @@ -19,7 +18,7 @@ data-cart-shipping-value="5" data-cart-tax-rate-value="8.25">

Shopping Cart (Stimulus Reactive + - TailwindUI)

+ Tailwind UI)
diff --git a/docs/lib/stimulus-reactive.js b/docs/lib/stimulus-reactive.js index e2008b1..632eaf3 100644 --- a/docs/lib/stimulus-reactive.js +++ b/docs/lib/stimulus-reactive.js @@ -6,14 +6,11 @@ function makeMap(str, expectsLowerCase) { } return expectsLowerCase ? (val) => !!map[val.toLowerCase()] : (val) => !!map[val]; } -const NOOP = () => { -}; const extend = Object.assign; const hasOwnProperty$1 = Object.prototype.hasOwnProperty; const hasOwn = (val, key) => hasOwnProperty$1.call(val, key); const isArray = Array.isArray; const isMap = (val) => toTypeString(val) === "[object Map]"; -const isFunction = (val) => typeof val === "function"; const isString = (val) => typeof val === "string"; const isSymbol = (val) => typeof val === "symbol"; const isObject = (val) => val !== null && typeof val === "object"; @@ -912,51 +909,6 @@ class RefImpl { } } -class ComputedRefImpl { - constructor(getter, _setter, isReadonly, isSSR) { - this._setter = _setter; - this.dep = void 0; - this.__v_isRef = true; - this["__v_isReadonly"] = false; - this._dirty = true; - this.effect = new ReactiveEffect(getter, () => { - if (!this._dirty) { - this._dirty = true; - triggerRefValue(this); - } - }); - this.effect.computed = this; - this.effect.active = this._cacheable = !isSSR; - this["__v_isReadonly"] = isReadonly; - } - get value() { - const self = toRaw(this); - trackRefValue(self); - if (self._dirty || !self._cacheable) { - self._dirty = false; - self._value = self.effect.run(); - } - return self._value; - } - set value(newValue) { - this._setter(newValue); - } -} -function computed(getterOrOptions, debugOptions, isSSR = false) { - let getter; - let setter; - const onlyGetter = isFunction(getterOrOptions); - if (onlyGetter) { - getter = getterOrOptions; - setter = NOOP; - } else { - getter = getterOrOptions.get; - setter = getterOrOptions.set; - } - const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR); - return cRef; -} - function getPropertyDescriptors(prototype, pattern, checkAncestors = true) { const matches = {}; while (prototype != null) { @@ -1069,12 +1021,7 @@ function useStimulusReactive(identifier, application) { function effect$1(fn) { this.__reactive.scope.run(() => effect(fn)); } - function computed$1(fn) { - const computedRef = this.__reactive.scope.run(() => computed(fn)); - return computedRef; - } Object.defineProperty(prototype, "effect", { value: effect$1 }); - Object.defineProperty(prototype, "computed", { value: computed$1 }); Object.defineProperty(prototype, "initialize", { value: initialize }); Object.defineProperty(prototype, "disconnect", { value: disconnect }); Object.keys(values).forEach((key) => valueChanged(key));