Skip to content

Commit

Permalink
docs(v1.0.0): computed to getter
Browse files Browse the repository at this point in the history
  • Loading branch information
ajaishankar committed Oct 17, 2023
1 parent f4071e2 commit 0a7bf8a
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 108 deletions.
44 changes: 20 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
}
}

Expand All @@ -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(() => ...);
}
}
```
Expand All @@ -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!

Expand Down
43 changes: 21 additions & 22 deletions docs/cart-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export default class extends Controller {
shipping: Number,
taxRate: Number,
};

static targets = [
"checkout",
"subtotal",
Expand All @@ -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");
Expand Down
11 changes: 5 additions & 6 deletions docs/cart-item-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
5 changes: 2 additions & 3 deletions docs/index.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<!DOCTYPE html>
<html lang="en"
class="h-screen bg-gray-50">
<html lang="en" class="h-screen">

<head>
<meta charset="utf-8" />
Expand All @@ -19,7 +18,7 @@
data-cart-shipping-value="5"
data-cart-tax-rate-value="8.25">
<h1 class="text-3xl font-bold tracking-tight text-gray-900 sm:text-4xl">Shopping Cart (Stimulus Reactive +
TailwindUI)</h1>
Tailwind UI)</h1>
<form class="mt-12 lg:grid lg:grid-cols-12 lg:items-start lg:gap-x-12 xl:gap-x-16">
<section aria-labelledby="cart-heading"
class="lg:col-span-7">
Expand Down
53 changes: 0 additions & 53 deletions docs/lib/stimulus-reactive.js
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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));
Expand Down

0 comments on commit 0a7bf8a

Please sign in to comment.