feat: add comprehensive State Management V2 documentation
- Add complete V2 decorators guide (state-management-v2.md, 38KB) - @ComponentV2, @Local, @Param, @Event - @ObservedV2, @Trace, @Computed, @Monitor - @Provider/@Consumer for cross-level sync - V1 vs V2 comparison and migration guide - Best practices and troubleshooting - Add 9 practical V2 code examples (state-management-v2-examples.ets, 13KB) - Basic components, nested objects, parent-child communication - Computed properties, monitors, providers/consumers - Real-world patterns: forms, todo lists, collections - Update SKILL.md with V2 decorators reference table - Source: Official OpenHarmony documentation (API 12+)
This commit is contained in:
@@ -34,6 +34,8 @@ struct HelloWorld {
|
|||||||
|
|
||||||
## State Management Decorators
|
## State Management Decorators
|
||||||
|
|
||||||
|
### V1 (Traditional)
|
||||||
|
|
||||||
| Decorator | Usage | Description |
|
| Decorator | Usage | Description |
|
||||||
|-----------|-------|-------------|
|
|-----------|-------|-------------|
|
||||||
| `@State` | `@State count: number = 0` | Component internal state |
|
| `@State` | `@State count: number = 0` | Component internal state |
|
||||||
@@ -42,6 +44,22 @@ struct HelloWorld {
|
|||||||
| `@Provide/@Consume` | Cross-level | Ancestor → Descendant |
|
| `@Provide/@Consume` | Cross-level | Ancestor → Descendant |
|
||||||
| `@Observed/@ObjectLink` | Nested objects | Deep object observation |
|
| `@Observed/@ObjectLink` | Nested objects | Deep object observation |
|
||||||
|
|
||||||
|
### V2 (Recommended - API 12+)
|
||||||
|
|
||||||
|
| Decorator | Usage | Description |
|
||||||
|
|-----------|-------|-------------|
|
||||||
|
| `@ComponentV2` | `@ComponentV2 struct MyComp` | Enable V2 state management |
|
||||||
|
| `@Local` | `@Local count: number = 0` | Internal state (no external init) |
|
||||||
|
| `@Param` | `@Param title: string = ""` | Parent → Child (one-way, efficient) |
|
||||||
|
| `@Event` | `@Event onChange: () => void` | Child → Parent (callback) |
|
||||||
|
| `@ObservedV2` | `@ObservedV2 class Data` | Class observation |
|
||||||
|
| `@Trace` | `@Trace name: string` | Property-level tracking |
|
||||||
|
| `@Computed` | `@Computed get value()` | Cached computed properties |
|
||||||
|
| `@Monitor` | `@Monitor('prop') onFn()` | Watch changes with before/after |
|
||||||
|
| `@Provider/@Consumer` | Cross-level | Two-way sync across tree |
|
||||||
|
|
||||||
|
**See [references/state-management-v2.md](references/state-management-v2.md) for complete V2 guide.**
|
||||||
|
|
||||||
## Common Layouts
|
## Common Layouts
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
@@ -277,6 +295,7 @@ See [references/arkguard-obfuscation.md](references/arkguard-obfuscation.md) for
|
|||||||
|
|
||||||
## Reference Files
|
## Reference Files
|
||||||
|
|
||||||
|
- **State Management V2**: [references/state-management-v2.md](references/state-management-v2.md) - Complete guide to V2 state management (@ComponentV2, @Local, @Param, @Event, @ObservedV2, @Trace, @Computed, @Monitor, @Provider, @Consumer)
|
||||||
- **Migration Guide**: [references/migration-guide.md](references/migration-guide.md) - Complete TypeScript to ArkTS migration rules and examples
|
- **Migration Guide**: [references/migration-guide.md](references/migration-guide.md) - Complete TypeScript to ArkTS migration rules and examples
|
||||||
- **Component Patterns**: [references/component-patterns.md](references/component-patterns.md) - Advanced component patterns and best practices
|
- **Component Patterns**: [references/component-patterns.md](references/component-patterns.md) - Advanced component patterns and best practices
|
||||||
- **API Reference**: [references/api-reference.md](references/api-reference.md) - Common HarmonyOS APIs
|
- **API Reference**: [references/api-reference.md](references/api-reference.md) - Common HarmonyOS APIs
|
||||||
|
|||||||
523
arkts-development/assets/state-management-v2-examples.ets
Normal file
523
arkts-development/assets/state-management-v2-examples.ets
Normal file
@@ -0,0 +1,523 @@
|
|||||||
|
// State Management V2 - Quick Reference Examples
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 1. BASIC COMPONENT WITH @Local STATE
|
||||||
|
// ============================================
|
||||||
|
@Entry
|
||||||
|
@ComponentV2
|
||||||
|
struct CounterApp {
|
||||||
|
@Local count: number = 0;
|
||||||
|
@Local step: number = 1;
|
||||||
|
|
||||||
|
build() {
|
||||||
|
Column({ space: 10 }) {
|
||||||
|
Text(`Count: ${this.count}`)
|
||||||
|
.fontSize(30)
|
||||||
|
Button(`+${this.step}`)
|
||||||
|
.onClick(() => this.count += this.step)
|
||||||
|
Button(`-${this.step}`)
|
||||||
|
.onClick(() => this.count -= this.step)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 2. NESTED OBJECTS WITH @ObservedV2/@Trace
|
||||||
|
// ============================================
|
||||||
|
@ObservedV2
|
||||||
|
class Address {
|
||||||
|
@Trace city: string;
|
||||||
|
@Trace street: string;
|
||||||
|
|
||||||
|
constructor(city: string, street: string) {
|
||||||
|
this.city = city;
|
||||||
|
this.street = street;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ObservedV2
|
||||||
|
class Person {
|
||||||
|
@Trace name: string;
|
||||||
|
@Trace age: number;
|
||||||
|
@Trace address: Address;
|
||||||
|
|
||||||
|
constructor(name: string, age: number, address: Address) {
|
||||||
|
this.name = name;
|
||||||
|
this.age = age;
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entry
|
||||||
|
@ComponentV2
|
||||||
|
struct PersonProfile {
|
||||||
|
person: Person = new Person(
|
||||||
|
"Tom",
|
||||||
|
25,
|
||||||
|
new Address("Beijing", "Main St")
|
||||||
|
);
|
||||||
|
|
||||||
|
build() {
|
||||||
|
Column({ space: 10 }) {
|
||||||
|
Text(`${this.person.name}, ${this.person.age}`)
|
||||||
|
Text(`${this.person.address.city}`)
|
||||||
|
Button('Birthday').onClick(() => {
|
||||||
|
this.person.age++; // Observable
|
||||||
|
})
|
||||||
|
Button('Move').onClick(() => {
|
||||||
|
this.person.address.city = "Shanghai"; // Observable
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 3. PARENT-CHILD WITH @Param/@Event
|
||||||
|
// ============================================
|
||||||
|
@ComponentV2
|
||||||
|
struct Counter {
|
||||||
|
@Param count: number = 0;
|
||||||
|
@Event onIncrement: () => void = () => {};
|
||||||
|
@Event onDecrement: () => void = () => {};
|
||||||
|
|
||||||
|
build() {
|
||||||
|
Row({ space: 10 }) {
|
||||||
|
Button('-').onClick(() => this.onDecrement())
|
||||||
|
Text(`${this.count}`).fontSize(30)
|
||||||
|
Button('+').onClick(() => this.onIncrement())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entry
|
||||||
|
@ComponentV2
|
||||||
|
struct ParentApp {
|
||||||
|
@Local count: number = 0;
|
||||||
|
|
||||||
|
build() {
|
||||||
|
Column({ space: 20 }) {
|
||||||
|
Text('Parent Count: ' + this.count)
|
||||||
|
Counter({
|
||||||
|
count: this.count,
|
||||||
|
onIncrement: () => this.count++,
|
||||||
|
onDecrement: () => this.count--
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 4. COMPUTED PROPERTIES
|
||||||
|
// ============================================
|
||||||
|
@ObservedV2
|
||||||
|
class CartItem {
|
||||||
|
@Trace name: string;
|
||||||
|
@Trace price: number;
|
||||||
|
@Trace quantity: number;
|
||||||
|
|
||||||
|
constructor(name: string, price: number, quantity: number) {
|
||||||
|
this.name = name;
|
||||||
|
this.price = price;
|
||||||
|
this.quantity = quantity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entry
|
||||||
|
@ComponentV2
|
||||||
|
struct ShoppingCart {
|
||||||
|
@Local items: CartItem[] = [
|
||||||
|
new CartItem("Apple", 5, 3),
|
||||||
|
new CartItem("Banana", 3, 5)
|
||||||
|
];
|
||||||
|
@Local taxRate: number = 0.1;
|
||||||
|
|
||||||
|
@Computed
|
||||||
|
get subtotal(): number {
|
||||||
|
console.log("Computing subtotal"); // Only logs when items change
|
||||||
|
return this.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Computed
|
||||||
|
get tax(): number {
|
||||||
|
return this.subtotal * this.taxRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Computed
|
||||||
|
get total(): number {
|
||||||
|
return this.subtotal + this.tax;
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
Column({ space: 10 }) {
|
||||||
|
ForEach(this.items, (item: CartItem) => {
|
||||||
|
Row() {
|
||||||
|
Text(`${item.name}: $${item.price} × ${item.quantity}`)
|
||||||
|
Button('+').onClick(() => item.quantity++)
|
||||||
|
}
|
||||||
|
}, (item: CartItem, idx: number) => item.name + idx)
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
Text(`Subtotal: $${this.subtotal.toFixed(2)}`)
|
||||||
|
Text(`Tax (${this.taxRate * 100}%): $${this.tax.toFixed(2)}`)
|
||||||
|
Text(`Total: $${this.total.toFixed(2)}`).fontWeight(FontWeight.Bold)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 5. MONITOR CHANGES
|
||||||
|
// ============================================
|
||||||
|
@ObservedV2
|
||||||
|
class Product {
|
||||||
|
@Trace name: string = "Laptop";
|
||||||
|
@Trace price: number = 1000;
|
||||||
|
@Trace stock: number = 10;
|
||||||
|
|
||||||
|
@Monitor('price')
|
||||||
|
onPriceChange(monitor: IMonitor) {
|
||||||
|
const change = monitor.value();
|
||||||
|
console.log(`Price changed from ${change?.before} to ${change?.now}`);
|
||||||
|
if (change && change.now > change.before) {
|
||||||
|
console.log(`Price increased by ${change.now - change.before}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Monitor('stock')
|
||||||
|
onStockChange(monitor: IMonitor) {
|
||||||
|
const change = monitor.value();
|
||||||
|
if (change && change.now < 5) {
|
||||||
|
console.warn(`Low stock alert: ${change.now} items`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entry
|
||||||
|
@ComponentV2
|
||||||
|
struct ProductManager {
|
||||||
|
product: Product = new Product();
|
||||||
|
|
||||||
|
@Monitor('product.price', 'product.stock')
|
||||||
|
onProductChange(monitor: IMonitor) {
|
||||||
|
console.log('Product properties changed:', monitor.dirty);
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
Column({ space: 10 }) {
|
||||||
|
Text(`${this.product.name}`)
|
||||||
|
Text(`Price: $${this.product.price}`)
|
||||||
|
Text(`Stock: ${this.product.stock}`)
|
||||||
|
|
||||||
|
Button('Increase Price').onClick(() => {
|
||||||
|
this.product.price += 100;
|
||||||
|
})
|
||||||
|
Button('Decrease Stock').onClick(() => {
|
||||||
|
this.product.stock--;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 6. PROVIDER/CONSUMER (CROSS-LEVEL)
|
||||||
|
// ============================================
|
||||||
|
@Entry
|
||||||
|
@ComponentV2
|
||||||
|
struct AppRoot {
|
||||||
|
@Provider('app-theme') theme: string = 'light';
|
||||||
|
@Provider('user-name') userName: string = 'Alice';
|
||||||
|
|
||||||
|
build() {
|
||||||
|
Column({ space: 20 }) {
|
||||||
|
Text(`App Theme: ${this.theme}`)
|
||||||
|
Button('Toggle Theme').onClick(() => {
|
||||||
|
this.theme = this.theme === 'light' ? 'dark' : 'light';
|
||||||
|
})
|
||||||
|
|
||||||
|
MiddleComponent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ComponentV2
|
||||||
|
struct MiddleComponent {
|
||||||
|
build() {
|
||||||
|
Column({ space: 10 }) {
|
||||||
|
Text('Middle Component')
|
||||||
|
DeepNestedComponent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ComponentV2
|
||||||
|
struct DeepNestedComponent {
|
||||||
|
@Consumer('app-theme') theme: string = 'default';
|
||||||
|
@Consumer('user-name') userName: string = 'Guest';
|
||||||
|
|
||||||
|
build() {
|
||||||
|
Column({ space: 10 }) {
|
||||||
|
Text(`User: ${this.userName}`)
|
||||||
|
Text(`Theme: ${this.theme}`)
|
||||||
|
.backgroundColor(this.theme === 'dark' ? Color.Black : Color.White)
|
||||||
|
.fontColor(this.theme === 'dark' ? Color.White : Color.Black)
|
||||||
|
|
||||||
|
Button('Change from Deep Component').onClick(() => {
|
||||||
|
this.theme = 'custom'; // Updates provider in AppRoot
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 7. FORM INPUT PATTERN
|
||||||
|
// ============================================
|
||||||
|
@ObservedV2
|
||||||
|
class FormData {
|
||||||
|
@Trace username: string = "";
|
||||||
|
@Trace email: string = "";
|
||||||
|
@Trace password: string = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@ComponentV2
|
||||||
|
struct FormField {
|
||||||
|
@Param label: string = "";
|
||||||
|
@Param value: string = "";
|
||||||
|
@Param type: InputType = InputType.Normal;
|
||||||
|
@Event onChange: (text: string) => void = () => {};
|
||||||
|
|
||||||
|
build() {
|
||||||
|
Column({ space: 5 }) {
|
||||||
|
Text(this.label).fontSize(14)
|
||||||
|
TextInput({ text: this.value })
|
||||||
|
.type(this.type)
|
||||||
|
.onChange((text: string) => this.onChange(text))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entry
|
||||||
|
@ComponentV2
|
||||||
|
struct RegistrationForm {
|
||||||
|
@Local formData: FormData = new FormData();
|
||||||
|
@Local isValid: boolean = false;
|
||||||
|
|
||||||
|
@Monitor('formData.username', 'formData.email', 'formData.password')
|
||||||
|
validateForm(monitor: IMonitor) {
|
||||||
|
this.isValid =
|
||||||
|
this.formData.username.length >= 3 &&
|
||||||
|
this.formData.email.includes('@') &&
|
||||||
|
this.formData.password.length >= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
Column({ space: 15 }) {
|
||||||
|
Text('Registration').fontSize(24).fontWeight(FontWeight.Bold)
|
||||||
|
|
||||||
|
FormField({
|
||||||
|
label: 'Username',
|
||||||
|
value: this.formData.username,
|
||||||
|
onChange: (text: string) => {
|
||||||
|
this.formData.username = text;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
FormField({
|
||||||
|
label: 'Email',
|
||||||
|
value: this.formData.email,
|
||||||
|
type: InputType.Email,
|
||||||
|
onChange: (text: string) => {
|
||||||
|
this.formData.email = text;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
FormField({
|
||||||
|
label: 'Password',
|
||||||
|
value: this.formData.password,
|
||||||
|
type: InputType.Password,
|
||||||
|
onChange: (text: string) => {
|
||||||
|
this.formData.password = text;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Button('Submit')
|
||||||
|
.enabled(this.isValid)
|
||||||
|
.backgroundColor(this.isValid ? Color.Blue : Color.Gray)
|
||||||
|
.onClick(() => {
|
||||||
|
console.log('Form submitted:', this.formData);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.padding(20)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 8. TODO LIST EXAMPLE
|
||||||
|
// ============================================
|
||||||
|
@ObservedV2
|
||||||
|
class TodoItem {
|
||||||
|
@Trace id: string;
|
||||||
|
@Trace title: string;
|
||||||
|
@Trace completed: boolean;
|
||||||
|
|
||||||
|
constructor(title: string) {
|
||||||
|
this.id = Date.now().toString() + Math.random();
|
||||||
|
this.title = title;
|
||||||
|
this.completed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ObservedV2
|
||||||
|
class TodoStore {
|
||||||
|
@Trace items: TodoItem[] = [];
|
||||||
|
|
||||||
|
addItem(title: string): void {
|
||||||
|
this.items.push(new TodoItem(title));
|
||||||
|
}
|
||||||
|
|
||||||
|
removeItem(id: string): void {
|
||||||
|
const index = this.items.findIndex(item => item.id === id);
|
||||||
|
if (index !== -1) {
|
||||||
|
this.items.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleItem(id: string): void {
|
||||||
|
const item = this.items.find(item => item.id === id);
|
||||||
|
if (item) {
|
||||||
|
item.completed = !item.completed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ComponentV2
|
||||||
|
struct TodoItemView {
|
||||||
|
@Param item: TodoItem = new TodoItem("");
|
||||||
|
@Event onToggle: () => void = () => {};
|
||||||
|
@Event onDelete: () => void = () => {};
|
||||||
|
|
||||||
|
build() {
|
||||||
|
Row({ space: 10 }) {
|
||||||
|
Checkbox({ select: this.item.completed })
|
||||||
|
.onChange(() => this.onToggle())
|
||||||
|
|
||||||
|
Text(this.item.title)
|
||||||
|
.decoration({ type: this.item.completed ? TextDecorationType.LineThrough : TextDecorationType.None })
|
||||||
|
.opacity(this.item.completed ? 0.5 : 1)
|
||||||
|
.layoutWeight(1)
|
||||||
|
|
||||||
|
Button('Delete')
|
||||||
|
.onClick(() => this.onDelete())
|
||||||
|
}
|
||||||
|
.padding(10)
|
||||||
|
.width('100%')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entry
|
||||||
|
@ComponentV2
|
||||||
|
struct TodoApp {
|
||||||
|
@Local store: TodoStore = new TodoStore();
|
||||||
|
@Local inputText: string = "";
|
||||||
|
|
||||||
|
@Computed
|
||||||
|
get completedCount(): number {
|
||||||
|
return this.store.items.filter(item => item.completed).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Computed
|
||||||
|
get totalCount(): number {
|
||||||
|
return this.store.items.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
Column({ space: 15 }) {
|
||||||
|
Text('Todo List').fontSize(30).fontWeight(FontWeight.Bold)
|
||||||
|
|
||||||
|
Text(`${this.completedCount} / ${this.totalCount} completed`)
|
||||||
|
|
||||||
|
Row({ space: 10 }) {
|
||||||
|
TextInput({ text: this.inputText, placeholder: 'New task...' })
|
||||||
|
.layoutWeight(1)
|
||||||
|
.onChange((text: string) => {
|
||||||
|
this.inputText = text;
|
||||||
|
})
|
||||||
|
|
||||||
|
Button('Add')
|
||||||
|
.enabled(this.inputText.trim().length > 0)
|
||||||
|
.onClick(() => {
|
||||||
|
if (this.inputText.trim()) {
|
||||||
|
this.store.addItem(this.inputText.trim());
|
||||||
|
this.inputText = "";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
List({ space: 5 }) {
|
||||||
|
ForEach(this.store.items, (item: TodoItem) => {
|
||||||
|
ListItem() {
|
||||||
|
TodoItemView({
|
||||||
|
item: item,
|
||||||
|
onToggle: () => this.store.toggleItem(item.id),
|
||||||
|
onDelete: () => this.store.removeItem(item.id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, (item: TodoItem) => item.id)
|
||||||
|
}
|
||||||
|
.layoutWeight(1)
|
||||||
|
}
|
||||||
|
.padding(20)
|
||||||
|
.height('100%')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 9. COLLECTION TYPES (Array, Map, Set)
|
||||||
|
// ============================================
|
||||||
|
@ObservedV2
|
||||||
|
class CollectionDemo {
|
||||||
|
@Trace numbers: number[] = [1, 2, 3];
|
||||||
|
@Trace userMap: Map<string, string> = new Map([['id1', 'Alice'], ['id2', 'Bob']]);
|
||||||
|
@Trace tags: Set<string> = new Set(['typescript', 'arkts', 'harmonyos']);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entry
|
||||||
|
@ComponentV2
|
||||||
|
struct CollectionsExample {
|
||||||
|
demo: CollectionDemo = new CollectionDemo();
|
||||||
|
|
||||||
|
build() {
|
||||||
|
Column({ space: 15 }) {
|
||||||
|
// Array
|
||||||
|
Text('Array:').fontWeight(FontWeight.Bold)
|
||||||
|
ForEach(this.demo.numbers, (num: number, idx: number) => {
|
||||||
|
Text(`[${idx}] = ${num}`)
|
||||||
|
}, (num: number, idx: number) => num.toString() + idx)
|
||||||
|
Button('Array.push').onClick(() => {
|
||||||
|
this.demo.numbers.push(Math.floor(Math.random() * 100));
|
||||||
|
})
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
|
||||||
|
// Map
|
||||||
|
Text('Map:').fontWeight(FontWeight.Bold)
|
||||||
|
ForEach(Array.from(this.demo.userMap.entries()), (entry: [string, string]) => {
|
||||||
|
Text(`${entry[0]}: ${entry[1]}`)
|
||||||
|
}, (entry: [string, string]) => entry[0])
|
||||||
|
Button('Map.set').onClick(() => {
|
||||||
|
const id = 'id' + Date.now();
|
||||||
|
this.demo.userMap.set(id, 'New User');
|
||||||
|
})
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
|
||||||
|
// Set
|
||||||
|
Text('Set:').fontWeight(FontWeight.Bold)
|
||||||
|
ForEach(Array.from(this.demo.tags.values()), (tag: string) => {
|
||||||
|
Text(`• ${tag}`)
|
||||||
|
}, (tag: string) => tag)
|
||||||
|
Button('Set.add').onClick(() => {
|
||||||
|
this.demo.tags.add('tag' + Date.now());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.padding(20)
|
||||||
|
}
|
||||||
|
}
|
||||||
1711
arkts-development/references/state-management-v2.md
Normal file
1711
arkts-development/references/state-management-v2.md
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user