Skip to content

Commit

Permalink
Continue working on README
Browse files Browse the repository at this point in the history
  • Loading branch information
mbeckem committed Apr 11, 2024
1 parent 4ea4596 commit 3831ecd
Showing 1 changed file with 131 additions and 2 deletions.
133 changes: 131 additions & 2 deletions packages/reactivity-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ Computed signals can be watched (e.g. via `effect()`) like any other signal:

```ts
import { reactive, computed, effect } from "@conterra/reactivity-core";

const age = reactive(21);
const doubleAge = computed(() => age.value * 2);

Expand All @@ -113,8 +114,134 @@ effect(() => {
age.value = 22;
```

Note that the callback function used by a computed signal should be stateless:
it is supposed to compute a value (possibly very often), and it should not change the state of the system while doing so.
Computed signals only re-compute their value (by invoking the callback function) when any of their dependencies have changed.
For as long as nothing has changed, the current value will be cached.
This can make even complex computed signals very efficient.

Note that the callback function for a computed signal should be stateless:
it is supposed to compute a value (possibly very often), and it should not change the state of the application while doing so.

### Using signals for reactive object properties

You can use signals in your classes (or single objects) to implement reactive objects.
For example:

```ts
import { reactive } from "@conterra/reactivity-core";

class Person {
// Could be private or public
_name = reactive("");

get name() {
return this._name.value;
}

set name(value) {
// Reactive write -> watches that used the `name` getter are notified.
// We could use this setter (which could also be a normal method) to enforce preconditions.
this._name.value = value;
}
}
```

Instances of person are now reactive, since their state is actually stored in signals:

```ts
import { effect } from "@conterra/reactivity-core";

// Person from previous example
const p = new Person();
p.name = "E. Example";

// Prints "E. Example"
effect(() => {
console.log(p.name);
});

// Triggers effect again; prints "T. Test"
p.name = "T. Test";
```

You can also provide computed values or accessor methods in your class:

```ts
import { reactive, computed } from "@conterra/reactivity-core";

// In this example, first name and last name can only be written to.
// Only the combined full name is available to users of the class.
class Person {
_firstName = reactive("");
_lastName = reactive("");
_fullName = computed(() => `${this._firstName.value} ${this._lastName.value}`);

setFirstName(name) {
this._firstName.value = name;
}

setLastName(name) {
this._lastName_.value = name;
}

getFullName() {
return this._fullName.value;
}
}
```

### Effect vs. Watch

We provide two different APIs to run code when reactive values change.
The simpler one is effect `effect()`:

```js
import { reactive, effect } from "@conterra/reactivity-core";

const r1 = reactive(0);
const r2 = reactive(1);
const r3 = reactive(2);

// Will run whenever _any_ of the given signals changed,
// even if the sum turns out to be the same.
effect(() => {
const sum = r1.value + r2.value + r3.value;
console.log(sum);
});
```

If your effect callbacks become more complex, it may be difficult to control which signals are ultimately used.
This can result in your effect running too often, because you're really only interested in _some_ changes and not all of them.

In that case, you can use `watch()`:

```js
import { reactive, watch } from "@conterra/reactivity-core";

const r1 = reactive(0);
const r2 = reactive(1);
const r3 = reactive(2);

watch(
// (1)
() => {
const sum = r1.value + r2.value + r3.value;
return [sum];
},
// (2)
([sum]) => {
console.log(sum);
}
);
```

`watch()` takes two functions:

- **(1)**: The _selector_ function. This function's body is tracked (like in `effect()`) and all its reactive dependencies are recorded. The function must return an array of values.
- **(2)**: The _callback_ function. This function is called whenever the selector function returned different values. The callback itself is _not_ reactive.

In this example, the callback function will only re-run when the computed sum truly changed.

### Reactive collections

## API

Expand All @@ -124,6 +251,8 @@ it is supposed to compute a value (possibly very often), and it should not chang

### Collections

### Types

## Gotchas

### Avoid side effects in computed signals
Expand Down

0 comments on commit 3831ecd

Please sign in to comment.