Skip to content

Commit

Permalink
docs(readme): update patterns
Browse files Browse the repository at this point in the history
  • Loading branch information
crimx committed Jan 26, 2024
1 parent 0c66a8f commit 2227bbc
Showing 1 changed file with 174 additions and 22 deletions.
196 changes: 174 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -374,9 +374,130 @@ const count$ = val(3, { eager: true });
const derived$ = derive(count$, count => count * 3, { eager: true });
```

## Use in Class
## Reactive Collections

The Reactive Collections are a group of classes that expand on the built-in JavaScript collections, allowing changes to the collections to be observed. See [docs](https://value-enhancer.js.org/modules/collections.html) for API details.

```ts
import { derive } from "value-enhancer";
import { ReactiveList } from "value-enhancer/collections";

const list = new ReactiveList(["a", "b", "c"]);

const item$ = derive(list.$, list => list[2]); // watch the item at index 2

console.log(item$.value); // "c"

list.set(2, "d");

console.log(item$.value); // "d"
```

```ts
import { val, flatten } from "value-enhancer";
import { ReactiveMap } from "value-enhancer/collections";

const map = new ReactiveMap();
const v = val("someValue");

const item$ = flatten(map.$, map => map.get("someKey")); // watch the item at "someKey"

console.log(item$.value); // undefined

map.set("someKey", v); // set a val, the value inside the val is subscribed and flatten to `item$`

console.log(item$.value); // "someValue"

v.set("someValue2"); // you can also set a non-val value, which is passed to `item$`` directly

console.log(item$.value); // "someValue2"
```

## Patterns

### Use in Class with different types.

With this pattern, Writable `Val` properties are exposed as `$` and `ReadonlyVal` properties are exposed as `$$`.

Note that they are all Writable `Val` under the hood. The difference is just the type.

```ts
import { val, type ReadonlyVal, type Val } from "value-enhancer";

interface MyClassVals {
a: number;
b: string;
}

export type MyClass$ = {
[K in keyof MyClassVals]: ReadonlyVal<MyClassVals[K]>;
};

export type MyClass$$ = {
[K in keyof MyClassVals]: Val<MyClassVals[K]>;
};

export class MyClass {
public readonly $: MyClass$;
public readonly $$: MyClassSet$;

public constructor() {
this.$ = this.$$ = {
a: val(1),
b: val("2"),
};
}
}

const myClass = new MyClass();
console.log(myClass.$.a.value);
myClass.$$.a.set(3);
```

### Use in Class with ReadonlyVal and setter

With `groupVals` you can easily create a group of ReadonlyVals and hide the setters.
If you want to ensure maximum safety and prevent others from modifying the value accidentally, you can use a real `ReadonlyVal`.

```ts
import {
readonlyVal,
type ReadonlyVal,
type ValSetValue,
} from "value-enhancer";

interface MyClassVals {
a: number;
b: string;
}

export type MyClass$ = {
[K in keyof MyClassVals]: ReadonlyVal<MyClassVals[K]>;
};

export type MyClassSet$ = {
[K in keyof MyClassVals]: ValSetValue<MyClassVals[K]>;
};

export class MyClass {
public readonly $: MyClass$;
public readonly set$: MyClass$$;

public constructor() {
const [a$, setA] = readonlyVal(1);
const [b$, setB] = readonlyVal("2");
this.$ = { a: a$, b: b$ };
this.set$ = { a: setA, b: setB };
}
}

const myClass = new MyClass();
console.log(myClass.$.a.value);
myClass.set$.a(3);
```

### Use in Class with GroupVals

Writing all these ReadonlyVals and setters could be cumbersome. With `groupVals` you can easily create a group of ReadonlyVals and setters.

```ts
import {
Expand All @@ -395,7 +516,7 @@ export interface Foo$ {

export class Foo {
public readonly $: Foo$;
private setVals: { [K in keyof Foo$]: ValSetValue<FlattenVal<Foo$[K]>> };
private setVals: { [K in keyof Foo$]: ValSetValue<UnwrapVal<Foo$[K]>> };

public constructor() {
const [vals, setVals] = groupVals({
Expand All @@ -420,41 +541,72 @@ foo.myMethod();
console.log(foo.$.a.value); // 2
```

## Reactive Collections
### Sharing vals to other Classes

The Reactive Collections are a group of classes that expand on the built-in JavaScript collections, allowing changes to the collections to be observed. See [docs](https://value-enhancer.js.org/modules/collections.html) for API details.
Sharing vals to other classes directly should be careful. Other classes may `dispose` the vals and cause unexpected behavior.

To share ReadonlyVals to other classes, use a derived val.

```ts
import { derive } from "value-enhancer";
import { ReactiveList } from "value-enhancer/collections";
import { val, derive, type ReadonlyVal } from "value-enhancer";

const list = new ReactiveList(["a", "b", "c"]);
interface AProps {
a$: ReadonlyVal<number>;
}

const item$ = derive(list.$, list => list[2]); // watch the item at index 2
class A {
a$: ReadonlyVal<number>;

console.log(item$.value); // "c"
constructor(props: AProps) {
this.a$ = props.a$;
}

list.set(2, "d");
dispose() {
this.a$.dispose();
}
}

console.log(item$.value); // "d"
const a$ = val(1);
const a = new A({ a$: derive(a$) });
a.dispose(); // will not affect a$
```

To share writable vals to other classes, use a ref val.

`Val.ref` creates a new Val referencing the value of the current Val as source.
All ref Vals share the same value from the source Val.
The act of setting a value on the ref Val is essentially setting the value on the source Val.

The ref Vals can be safely disposed without affecting the source Val and other ref Vals.

```ts
import { val, flatten } from "value-enhancer";
import { ReactiveMap } from "value-enhancer/collections";
import { val, type Val } from "value-enhancer";

const map = new ReactiveMap();
const v = val("someValue");
interface AProps {
a$: Val<number>;
}

const item$ = flatten(map.$, map => map.get("someKey")); // watch the item at "someKey"
class A {
a$: Val<number>;

console.log(item$.value); // undefined
constructor(props: AProps) {
this.a$ = props.a$;
}

map.set("someKey", v); // set a val, the value inside the val is subscribed and flatten to `item$`
dispose() {
this.a$.dispose();
}
}

console.log(item$.value); // "someValue"
const a$ = val(1);
const a1 = new A({ a$: a$.ref() });
const a2 = new A({ a$: a$.ref() });

v.set("someValue2"); // you can also set a non-val value, which is passed to `item$`` directly
a2.a$.set(2);

console.log(item$.value); // "someValue2"
console.log(a$.value); // 2
console.log(a1.a$.value); // 2
console.log(a2.a$.value); // 2

a1.dispose(); // will not affect a$ and a2.a$
```

0 comments on commit 2227bbc

Please sign in to comment.