diff --git a/packages/reactivity-core/README.md b/packages/reactivity-core/README.md index 1cc9e30..208f3eb 100644 --- a/packages/reactivity-core/README.md +++ b/packages/reactivity-core/README.md @@ -331,7 +331,81 @@ map.set("foo", "bar"); // effect prints "bar" #### Struct -TODO +Previously we described how you can create objects with reactive properties using `reactive` and `computed`. +For example a person having a first name, a last name and computed property computing the full name, whenever first or last name changes. + +The reactivity API helps you to create such reactive objects by providing a function called `reactiveStruct`. + +For example, to create a person with `reactiveStruct` proceed as follows: + +```ts +import { reactiveStruct } from "@conterra/reactivity-core"; + +// declare a type for the reactive object +type PersonType = { + firstName: string; + lastName: string; +} +// define a class like PersonClass +const PersonClass = reactiveStruct().define({ + firstName: {}, // default options (reactive and writable) + lastName: { writable: false } // read-only +}); +// create a new reactive instance +const person = new PersonClass({ + firstName: "John", + lastName: "Doe" +}); +// compute the full name +const fullName = computed(() => `${person.firstName} ${person.lastName}`); +console.log(fullName.value); // John Doe +person.firstName = "Jane"; +console.log(fullName.value); // Jane Doe +``` +The `define` function can be used to +- make properties read-only +- declare non reactive properties +- create computed properties +- add methods to the reactive object + +The following example shows declaring an extended `Person`: + +```ts +type PersonType = { + firstName: string; + lastName: string; + fullName: string; // will be a computed property + printName: () => void // a method printing the full name +} +const PersonClass = reactiveStruct().define({ + firstName: {}, + lastName: { writable: false }, + fullName: { + compute() { + // executed whenever first or last name changes + return `${this.firstName} ${this.lastName}`; + } + }, + printName: { + method() { + // always prints the current full name + console.log(`My name is ${this.fullName}`); + } + } +}); +// create a new reactive instance +const person = new PersonClass({ + firstName: "John", + lastName: "Doe" +}); +person.printName(); // My name is John Doe +person.firstName = "Jane"; +person.printName(); // My name is Jane Doe +``` + +Reactive structs are class like, as they don't support inheritance for example. +You can imagine reactive structs as simple objects having reactive properties, computed properties and methods. +Please consider writing your own classes with the basic API like `reactive` if a _class like_ is not enough. ## Why? diff --git a/packages/reactivity-core/struct/struct.ts b/packages/reactivity-core/struct/struct.ts index 857301e..7e6ba85 100644 --- a/packages/reactivity-core/struct/struct.ts +++ b/packages/reactivity-core/struct/struct.ts @@ -186,12 +186,12 @@ export interface ReactiveStructBuilder { * * 3. Create a new reactive struct class based on the provided definition. * ```ts - * const PersonModel = reactiveStruct().define(personDefinition); + * const PersonClass = reactiveStruct().define(personDefinition); * ``` * * 4. Create a new instance of the struct. * ```ts - * const person = new PersonModel({ + * const person = new PersonClass({ * firstName: "John", * lastName: "Doe" * }); @@ -215,6 +215,10 @@ export interface ReactiveStructBuilder { * * To define a read-only property set `writable` to `false`: * ```ts + * type PersonType = { + * firstName: string; + * readonly lastName: string; + * } * const personDefinition: ReactiveStructDefinition = { * firstName: {}, * lastName: { writable: false } @@ -224,7 +228,7 @@ export interface ReactiveStructBuilder { * firstName: "John", * lastName: "Doe" * }); - * person.lastName = "Smith"; // throws an error + * person.lastName = "Smith"; // type error, throws error at runtime * ``` * * To define a non reactive property set `reactive` to `false`: @@ -263,8 +267,8 @@ export interface ReactiveStructBuilder { * } * } * }; - * const PersonModel = reactiveStruct().define(personDefinition); - * const person = new PersonModel({ + * const PersonClass = reactiveStruct().define(personDefinition); + * const person = new PersonClass({ * firstName: "John", * lastName: "Doe" * }); @@ -292,8 +296,8 @@ export interface ReactiveStructBuilder { * } * } * }; - * const PersonModel = reactiveStruct().define(personDefinition); - * const person = new PersonModel({ + * const PersonClass = reactiveStruct().define(personDefinition); + * const person = new PersonClass({ * firstName: "John", * lastName: "Doe" * });