ueca-react 1.0.7 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +109 -119
- package/dist/index.d.ts +266 -4
- package/dist/ueca-react.js +1453 -0
- package/docs/Arrays and Reactivity in UECA-React.md +158 -0
- package/docs/Automatic onChange Events in UECA-React.md +142 -0
- package/docs/Automatic onChanging Events in UECA-React.md +157 -0
- package/docs/Automatic onPropChange and onPropChanging Events in UECA-React.md +112 -0
- package/docs/Component Extension in UECA-React.md +275 -0
- package/docs/Component IDs in UECA-React.md +181 -0
- package/docs/{component-intergation-model.md → Component Integration Model in UECA-React.md } +4 -3
- package/docs/{component-mental-model.md → Component Mental Model in UECA-React.md } +4 -3
- package/docs/Introduction to UECA-React Components.md +190 -0
- package/docs/Introduction to UECA-React.md +24 -0
- package/docs/Lifecycle Hooks in UECA-React.md +237 -0
- package/docs/Message Bus in UECA-React.md +260 -0
- package/docs/Model Caching in UECA-React.md +144 -0
- package/docs/Property Bindings in UECA-React.md +191 -0
- package/docs/State Management in UECA-React.md +128 -0
- package/docs/Technology of UECA-React.md +45 -0
- package/docs/Tracing in UECA-React.md +110 -0
- package/docs/code-template.md +53 -27
- package/docs/index.md +31 -11
- package/package.json +68 -72
- package/dist/componentModel.d.ts +0 -127
- package/dist/componentModel.js +0 -772
- package/dist/dynamicContent.d.ts +0 -22
- package/dist/dynamicContent.js +0 -80
- package/dist/index.js +0 -29
- package/dist/messageBus.d.ts +0 -46
- package/dist/messageBus.js +0 -141
- package/dist/utils.d.ts +0 -8
- package/dist/utils.js +0 -52
- package/docs/base-concepts.md +0 -192
- package/docs/bindings-overview.md +0 -164
- package/docs/general-code-structure.md +0 -177
- package/docs/introduction.md +0 -56
- package/docs/message-bus.md +0 -177
- package/docs/technology.md +0 -45
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Arrays and Reactivity in UECA-React
|
|
2
|
+
|
|
3
|
+
In UECA-React, arrays are a fundamental part of managing collections of data, such as lists of items in a user interface. UECA-React enhances arrays with reactivity, ensuring that changes to the array or its items can automatically trigger updates in the UI. This article explains how arrays work in UECA-React, their reactivity model, and the role of the `UECA.observe()` utility in making array items observable.
|
|
4
|
+
|
|
5
|
+
## Shallow Reactivity of Arrays
|
|
6
|
+
|
|
7
|
+
Arrays in UECA-React are **shallowly reactive**. This means that the array itself is reactive for operations that change its structure, such as adding or removing items. For example, methods like `push`, `pop`, `splice`, and direct index assignments (e.g., `array[0] = value`) are automatically detected, and the UI updates accordingly.
|
|
8
|
+
|
|
9
|
+
However, shallow reactivity only applies to the array's structure, not to the properties of the objects inside the array. If you modify a property of an object within the array (e.g., `array[0].property = newValue`), this change will **not** trigger a UI update unless the object itself is made observable.
|
|
10
|
+
|
|
11
|
+
### Example of Shallow Reactivity
|
|
12
|
+
|
|
13
|
+
Consider an array of tasks in a TODO list:
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
const tasks = [
|
|
17
|
+
{ id: 1, description: 'Task 1', done: false },
|
|
18
|
+
{ id: 2, description: 'Task 2', done: true }
|
|
19
|
+
];
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- **Reactive operations**: Adding a new task with `tasks.push({ id: 3, description: 'Task 3', done: false })` or removing a task with `tasks.splice(0, 1)` will trigger a UI update.
|
|
23
|
+
- **Non-reactive operations**: Changing a task's `done` status with `tasks[0].done = true` will **not** trigger a UI update because the task object is not observable.
|
|
24
|
+
|
|
25
|
+
## Making Array Items Observable with `UECA.observe()`
|
|
26
|
+
|
|
27
|
+
To make changes to the properties of array items reactive, you need to wrap each item with `UECA.observe()`. This utility function makes an object's properties observable, ensuring that any modifications to those properties trigger the necessary UI updates.
|
|
28
|
+
|
|
29
|
+
### Usage of `UECA.observe()`
|
|
30
|
+
|
|
31
|
+
When adding items to an array, wrap each item with `UECA.observe()` to make its properties reactive:
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
const tasks = [
|
|
35
|
+
UECA.observe({ id: 1, description: 'Task 1', done: false }),
|
|
36
|
+
UECA.observe({ id: 2, description: 'Task 2', done: true })
|
|
37
|
+
];
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Now, both structural changes to the array and property changes to the items will trigger UI updates:
|
|
41
|
+
|
|
42
|
+
- **Array-level reactivity**: `tasks.push(UECA.observe({ id: 3, description: 'Task 3', done: false }))` adds a new task and updates the UI.
|
|
43
|
+
- **Item-level reactivity**: `tasks[0].done = true` changes the `done` property of the first task and triggers a UI update because the task is observable.
|
|
44
|
+
|
|
45
|
+
### Why Use `UECA.observe()`?
|
|
46
|
+
|
|
47
|
+
- **Efficient updates**: By making array items observable, you ensure that only the affected parts of the UI re-render when a property changes, rather than re-rendering the entire list.
|
|
48
|
+
- **Simplified state management**: You can directly modify item properties without needing to create new copies of the array or use immutable update patterns.
|
|
49
|
+
|
|
50
|
+
## Code Example: Reactive TODO List
|
|
51
|
+
|
|
52
|
+
Below is an example of a TODO list that demonstrates how to use reactive arrays and observable items in UECA-React.
|
|
53
|
+
|
|
54
|
+
### Component Structure
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
type Task = {
|
|
58
|
+
taskId: number;
|
|
59
|
+
description: string;
|
|
60
|
+
done: boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
type TodoListStruct = UECA.ComponentStruct<{
|
|
64
|
+
props: {
|
|
65
|
+
newTask: string;
|
|
66
|
+
tasks: Task[];
|
|
67
|
+
};
|
|
68
|
+
methods: {
|
|
69
|
+
addTask: () => void;
|
|
70
|
+
deleteTask: (taskId: number) => void;
|
|
71
|
+
toggleTask: (taskId: number) => void;
|
|
72
|
+
taskListView: () => JSX.Element;
|
|
73
|
+
};
|
|
74
|
+
}>;
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Implementation
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
function useTodoList(params?: TodoListParams): TodoListModel {
|
|
81
|
+
const struct: TodoListStruct = {
|
|
82
|
+
props: {
|
|
83
|
+
id: useTodoList.name,
|
|
84
|
+
newTask: "",
|
|
85
|
+
tasks: []
|
|
86
|
+
},
|
|
87
|
+
methods: {
|
|
88
|
+
addTask: () => {
|
|
89
|
+
const newTaskName = model.newTask.trim();
|
|
90
|
+
if (newTaskName) {
|
|
91
|
+
model.tasks.push(UECA.observe({
|
|
92
|
+
description: newTaskName,
|
|
93
|
+
done: false,
|
|
94
|
+
taskId: Math.round(Math.random() * 1000000)
|
|
95
|
+
}));
|
|
96
|
+
model.newTask = "";
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
deleteTask: (taskId: number) => {
|
|
100
|
+
model.tasks = model.tasks.filter(task => task.taskId !== taskId);
|
|
101
|
+
},
|
|
102
|
+
toggleTask: (taskId: number) => {
|
|
103
|
+
const task = model.tasks.find(task => task.taskId === taskId);
|
|
104
|
+
if (task) {
|
|
105
|
+
task.done = !task.done; // Directly mutate the observable task
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
taskListView: () => (
|
|
109
|
+
<ul>
|
|
110
|
+
{model.tasks.map(task => (
|
|
111
|
+
<li key={task.taskId}>
|
|
112
|
+
<TodoItem
|
|
113
|
+
id={`task${task.taskId}`}
|
|
114
|
+
task={task}
|
|
115
|
+
onDelete={() => model.deleteTask(task.taskId)}
|
|
116
|
+
onToggle={() => model.toggleTask(task.taskId)}
|
|
117
|
+
/>
|
|
118
|
+
</li>
|
|
119
|
+
))}
|
|
120
|
+
</ul>
|
|
121
|
+
)
|
|
122
|
+
},
|
|
123
|
+
View: () => (
|
|
124
|
+
<div id={model.htmlId()}>
|
|
125
|
+
<div>
|
|
126
|
+
<input
|
|
127
|
+
type="text"
|
|
128
|
+
value={model.newTask}
|
|
129
|
+
onChange={(e) => (model.newTask = e.target.value)}
|
|
130
|
+
placeholder="New task"
|
|
131
|
+
/>
|
|
132
|
+
<button onClick={model.addTask}>Add</button>
|
|
133
|
+
</div>
|
|
134
|
+
<model.taskListView />
|
|
135
|
+
</div>
|
|
136
|
+
)
|
|
137
|
+
};
|
|
138
|
+
const model = UECA.useComponent(struct, params);
|
|
139
|
+
return model;
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Explanation
|
|
144
|
+
|
|
145
|
+
- **Adding a task**: When a new task is added, it is wrapped with `UECA.observe()` to make its properties (`description`, `done`) reactive.
|
|
146
|
+
- **Toggling a task**: The `toggleTask` method directly mutates the `done` property of the observable task, triggering a UI update for that specific task.
|
|
147
|
+
- **Deleting a task**: The `deleteTask` method filters the array, removing the task and updating the UI due to the array's shallow reactivity.
|
|
148
|
+
|
|
149
|
+
## Best Practices
|
|
150
|
+
|
|
151
|
+
- **Use `UECA.observe()` for item-level reactivity**: Apply it to array items when you need to track changes to their properties.
|
|
152
|
+
- **Avoid unnecessary observability**: If you only need to track structural changes to the array (e.g., adding/removing items), you can skip `UECA.observe()` for the items.
|
|
153
|
+
- **Performance considerations**: For large arrays or frequent updates, be mindful of the performance impact of making many items observable. Test and optimize as needed.
|
|
154
|
+
- **Keep the main `View` simple**: Use methods like `taskListView` to handle complex rendering logic, keeping the main `View` clean and focused.
|
|
155
|
+
|
|
156
|
+
## Conclusion
|
|
157
|
+
|
|
158
|
+
In UECA-React, arrays are shallowly reactive, meaning changes to the array's structure automatically trigger UI updates. However, to make changes to the properties of array items reactive, you must wrap those items with `UECA.observe()`. This approach ensures efficient and targeted UI updates, simplifying state management and improving performance. By understanding and applying these concepts, developers can build dynamic and responsive applications with ease.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Automatic onChange\<Prop> Events in UECA-React
|
|
2
|
+
#events #reactivity #state #onchange
|
|
3
|
+
|
|
4
|
+
## Description
|
|
5
|
+
The automatic `onChange<Prop>` feature in UECA-React simplifies state management by generating event handlers for each property defined in a component's `props` section. These handlers, named in the format `onChange<PropertyName>` (e.g., `onChangeCaption` for a `caption` property), are triggered whenever the corresponding property in the model is modified. This feature leverages UECA’s reactive model and MobX integration to enable components to respond to state changes without requiring manual event handler definitions, enhancing code efficiency and maintainability.
|
|
6
|
+
|
|
7
|
+
Key aspects of `onChange<Prop>` events:
|
|
8
|
+
- **Automatic Generation**: Event handlers are created for every property in `props` without explicit declaration in component structure.
|
|
9
|
+
- **Reactive**: Triggered by any change to the property, ensuring real-time updates.
|
|
10
|
+
- **Type-Safe**: Fully integrated with TypeScript for robust typing and error prevention.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## API/Methods
|
|
15
|
+
|
|
16
|
+
### Automatic Event Handlers
|
|
17
|
+
- **onChange\<PropertyName>**
|
|
18
|
+
An event handler automatically generated for each property in the `props` section of the component structure. It is triggered when the property’s value changes.
|
|
19
|
+
- **Signature**: `(newValue: PropType, oldValue: PropType) => void`
|
|
20
|
+
- `newValue`: The updated value of the property.
|
|
21
|
+
- `oldValue`: Previous value of the property.
|
|
22
|
+
- **Usage**: Defined in the `events` section or passed as a prop to child components to react to state changes.
|
|
23
|
+
|
|
24
|
+
### Triggering Events
|
|
25
|
+
- Property changes in the model (e.g., `model.caption = "new value"`) automatically invoke the corresponding `onChange<Prop>` handler if defined.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Code Examples
|
|
30
|
+
|
|
31
|
+
### Example: Handling Property Changes with onChange
|
|
32
|
+
This example shows a component that uses the automatic `onChangeCaption` event to log changes to its `caption` property.
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import * as UECA from "ueca-react";
|
|
36
|
+
|
|
37
|
+
type ButtonStruct = UECA.ComponentStruct<{
|
|
38
|
+
props: {
|
|
39
|
+
caption: string
|
|
40
|
+
}
|
|
41
|
+
}>;
|
|
42
|
+
|
|
43
|
+
type ButtonParams = UECA.ComponentParams<ButtonStruct>;
|
|
44
|
+
type ButtonModel = UECA.ComponentModel<ButtonStruct>;
|
|
45
|
+
|
|
46
|
+
function useButton(params?: ButtonParams): ButtonModel {
|
|
47
|
+
const struct: ButtonStruct = {
|
|
48
|
+
props: {
|
|
49
|
+
caption: "Click Me"
|
|
50
|
+
},
|
|
51
|
+
events: {
|
|
52
|
+
// Automatic OnChange event
|
|
53
|
+
onChangeCaption: (newValue, oldValue) => {
|
|
54
|
+
console.log(`Caption changed from "${oldValue}" to "${newValue}"`);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
View: () =>
|
|
58
|
+
<div id={model.htmlId()}>
|
|
59
|
+
<button onClick={() => model.caption = "Updated!"}>
|
|
60
|
+
{model.caption}
|
|
61
|
+
</button>
|
|
62
|
+
</div>
|
|
63
|
+
};
|
|
64
|
+
const model = UECA.useComponent(struct, params);
|
|
65
|
+
return model;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const Button = UECA.getFC(useButton);
|
|
69
|
+
export { ButtonModel, useButton, Button };
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
- **Explanation**:
|
|
73
|
+
- The `caption` property is defined in `props`.
|
|
74
|
+
- The `onChangeCaption` event handler is automatically available and logs changes.
|
|
75
|
+
- Clicking the button updates `model.caption` and triggers `onChangeCaption`.
|
|
76
|
+
|
|
77
|
+
### Example: Two-Way Binding with onChange
|
|
78
|
+
This example demonstrates using `onChange<Prop>` in a parent-child scenario with a form input.
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import * as UECA from "ueca-react";
|
|
82
|
+
|
|
83
|
+
type FormStruct = UECA.ComponentStruct<{
|
|
84
|
+
props: {
|
|
85
|
+
userName: string
|
|
86
|
+
};
|
|
87
|
+
children: {
|
|
88
|
+
input: InputModel
|
|
89
|
+
};
|
|
90
|
+
}>;
|
|
91
|
+
|
|
92
|
+
type FormParams = UECA.ComponentParams<FormStruct>;
|
|
93
|
+
type FormModel = UECA.ComponentModel<FormStruct>;
|
|
94
|
+
|
|
95
|
+
function useForm(params?: FormParams): FormModel {
|
|
96
|
+
const struct: FormStruct = {
|
|
97
|
+
props: {
|
|
98
|
+
userName: ""
|
|
99
|
+
},
|
|
100
|
+
events: {
|
|
101
|
+
// Automatic OnChange event for "userName" property
|
|
102
|
+
onChangeUserName: (newValue, oldValue) => {
|
|
103
|
+
console.log(`User name changed from "${oldValue}" to "${newValue}"`);
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
children: {
|
|
107
|
+
input: useInput({
|
|
108
|
+
value: UECA.bind(() => model, "userName"),
|
|
109
|
+
// Automatic OnChange event for "value" property
|
|
110
|
+
onChangeValue: (newValue, oldValue) => {
|
|
111
|
+
console.log(`Input value changed from "${oldValue}" to "${newValue}"`);
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
},
|
|
115
|
+
View: () =>
|
|
116
|
+
<div id={model.htmlId()}>
|
|
117
|
+
<model.input.View />
|
|
118
|
+
</div>
|
|
119
|
+
};
|
|
120
|
+
const model = UECA.useComponent(struct, params);
|
|
121
|
+
return model;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const Form = UECA.getFC(useForm);
|
|
125
|
+
export { FormModel, useForm, Form };
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
- **Explanation**:
|
|
129
|
+
- The parent’s `userName` is bound to the child’s `value` prop.
|
|
130
|
+
- The child’s input updates the parent’s `userName`, triggering firstly child’s `onChangeValue` and then parent’s `onChangeUserName` to log the changes.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Best Practices
|
|
135
|
+
- **Use for Reactive Updates**: Leverage `onChange<Prop>` events to respond to state changes in real-time, such as logging or triggering side effects.
|
|
136
|
+
- **Combine with Bindings**: Use `onChange<Prop>` with bidirectional bindings for interactive components like forms to ensure state consistency.
|
|
137
|
+
- **Keep Handlers Lightweight**: Avoid heavy computations in `onChange<Prop>` handlers to maintain performance.
|
|
138
|
+
|
|
139
|
+
## Notes
|
|
140
|
+
- `onChange<Prop>` events are automatically declared/generated for all properties in `props`, reducing boilerplate code.
|
|
141
|
+
- These events integrate seamlessly with UECA’s MobX-based reactivity system.
|
|
142
|
+
- For non-property-based communication, consider the UECA Message Bus.
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Automatic onChanging\<Prop> Events in UECA-React
|
|
2
|
+
#events #reactivity #validation #state #onchanging
|
|
3
|
+
|
|
4
|
+
## Description
|
|
5
|
+
The `onChanging<Prop>` event in UECA-React is an automatically generated event that triggers **before** a property's value is updated in a component's state. This pre-update hook allows developers to intercept, validate, or modify the proposed change, offering fine-grained control over state transitions. Unlike the `onChange<Prop>` event, which fires after a property has been updated, `onChanging<Prop>` provides an opportunity to enforce rules or perform side effects before the state is altered.
|
|
6
|
+
|
|
7
|
+
Key features:
|
|
8
|
+
- **Automatic Generation**: Event handlers are created for every property in `props` without explicit declaration in component structure.
|
|
9
|
+
- **Preemptive Control**: Runs before the property update, enabling validation or cancellation.
|
|
10
|
+
- **Flexible**: Can return a modified value or proceed with the original value that blocks the change.
|
|
11
|
+
- **Reactive Integration**: Works seamlessly with UECA-React’s reactive system and MobX.
|
|
12
|
+
- **Type-Safe**: Built with TypeScript support for reliable development.
|
|
13
|
+
|
|
14
|
+
This event is ideal for scenarios like input validation, data transformation, or preventing invalid state changes.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## API/Methods
|
|
19
|
+
|
|
20
|
+
### Event Handler
|
|
21
|
+
- **onChanging\<PropertyName>**
|
|
22
|
+
Automatically generated for each property defined in the `props` section.
|
|
23
|
+
- **Signature**: `(newValue: PropType, oldValue: PropType) => PropType`
|
|
24
|
+
- `newValue`: The proposed value for the property.
|
|
25
|
+
- `oldValue`: Current value of the property.
|
|
26
|
+
- **Return**:
|
|
27
|
+
- A value to apply.
|
|
28
|
+
- `oldValue` to block the change.
|
|
29
|
+
- **Usage**: Defined in the `events` section or passed as a prop to child components to react to state changes.
|
|
30
|
+
|
|
31
|
+
### Triggering the Event
|
|
32
|
+
- The event fires whenever a property is modified (e.g., `model.prop = value`), before the change is committed to the state.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Code Examples
|
|
37
|
+
|
|
38
|
+
### Example 1: Validating Input
|
|
39
|
+
This example shows a component that ensures the `quantity` property is between 0 and 10.
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import * as UECA from "ueca-react";
|
|
43
|
+
|
|
44
|
+
type CounterStruct = UECA.ComponentStruct<{
|
|
45
|
+
props: {
|
|
46
|
+
quantity: number
|
|
47
|
+
};
|
|
48
|
+
}>;
|
|
49
|
+
|
|
50
|
+
type CounterParams = UECA.ComponentParams<CounterStruct>;
|
|
51
|
+
type CounterModel = UECA.ComponentModel<CounterStruct>;
|
|
52
|
+
|
|
53
|
+
function useCounter(params?: CounterParams): CounterModel {
|
|
54
|
+
const struct: CounterStruct = {
|
|
55
|
+
props: {
|
|
56
|
+
quantity: 0
|
|
57
|
+
},
|
|
58
|
+
events: {
|
|
59
|
+
onChangingQuantity: (newValue, oldValue) => {
|
|
60
|
+
if (newValue < 0 || newValue > 10) {
|
|
61
|
+
console.error("Quantity cannot be negative or greater than 10");
|
|
62
|
+
return oldValue; // Prevents the update
|
|
63
|
+
}
|
|
64
|
+
return newValue;
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
View: () =>
|
|
68
|
+
<div id={model.htmlId()}>
|
|
69
|
+
<p>Quantity: {model.quantity}</p>
|
|
70
|
+
<button onClick={() => model.quantity++}>
|
|
71
|
+
{/* Clicking triggers onChangingQuantity event */}
|
|
72
|
+
Increment Quantity
|
|
73
|
+
</button>
|
|
74
|
+
<button onClick={() => model.quantity--}>
|
|
75
|
+
{/* Clicking triggers onChangingQuantity event */}
|
|
76
|
+
Decriment Quantity
|
|
77
|
+
</button>
|
|
78
|
+
</div>
|
|
79
|
+
};
|
|
80
|
+
const model = UECA.useComponent(struct, params);
|
|
81
|
+
return model;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const Counter = UECA.getFC(useCounter);
|
|
85
|
+
export { CounterModel, useCounter, Counter };
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
- **Explanation**:
|
|
89
|
+
- The `onChangingQuantity` handler checks if the new value is between 0 and 10.
|
|
90
|
+
- If it is not, the update is blocked by returning the `oldValue`.
|
|
91
|
+
- Otherwise, the `newValue` is returned and update the property.
|
|
92
|
+
|
|
93
|
+
### Example 2: Transforming Input
|
|
94
|
+
This example transforms a `text` property to uppercase before applying it.
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import * as UECA from "ueca-react";
|
|
98
|
+
|
|
99
|
+
type TextInputStruct = UECA.ComponentStruct<{
|
|
100
|
+
props: {
|
|
101
|
+
text: string
|
|
102
|
+
};
|
|
103
|
+
}>;
|
|
104
|
+
|
|
105
|
+
type TextInputParams = UECA.ComponentParams<TextInputStruct>;
|
|
106
|
+
type TextInputModel = UECA.ComponentModel<TextInputStruct>;
|
|
107
|
+
|
|
108
|
+
function useTextInput(params?: TextInputParams): TextInputModel {
|
|
109
|
+
const struct: TextInputStruct = {
|
|
110
|
+
props: {
|
|
111
|
+
text: ""
|
|
112
|
+
},
|
|
113
|
+
events: {
|
|
114
|
+
onChangingText: (newValue) => {
|
|
115
|
+
return newValue.toUpperCase(); // Transform to uppercase
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
View: () =>
|
|
119
|
+
<div id={model.htmlId()}>
|
|
120
|
+
{/* Input value update triggers onChangingText event */}
|
|
121
|
+
<input
|
|
122
|
+
type="text"
|
|
123
|
+
value={model.text}
|
|
124
|
+
onChange={(e) => model.text = e.target.value}
|
|
125
|
+
/>
|
|
126
|
+
</div>
|
|
127
|
+
};
|
|
128
|
+
const model = UECA.useComponent(struct, params);
|
|
129
|
+
return model;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const TextInput = UECA.getFC(useTextInput);
|
|
133
|
+
export { TextInputModel, useTextInput, TextInput };
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
- **Explanation**:
|
|
137
|
+
- The `onChangingText` handler converts the input to uppercase.
|
|
138
|
+
- The transformed value is then set as the new `text` property.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Best Practices
|
|
143
|
+
- **Validation**: Use `onChanging<Prop>` to enforce constraints (e.g., positive numbers, valid formats).
|
|
144
|
+
- **Lightweight Logic**: Keep handlers fast and simple to avoid performance issues.
|
|
145
|
+
- **Pair with onChange**: Combine with `onChange<Prop>` for pre- and post-update logic.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Comparison to Traditional React
|
|
150
|
+
In traditional React, you might handle validation in an `onChange` handler after the state updates, requiring additional logic to revert invalid changes. With UECA-React’s `onChanging<Prop>`, you can prevent invalid updates proactively, reducing complexity and improving performance.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Notes
|
|
155
|
+
- Handlers are optional; if not defined, property updates proceed as normal.
|
|
156
|
+
- The event integrates with UECA’s reactivity, ensuring efficient state management.
|
|
157
|
+
- Returning `oldValue` explicitly cancels the update.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Automatic onPropChange and onPropChanging Events in UECA-React
|
|
2
|
+
#events #reactivity #state #onpropchange #onpropchanging
|
|
3
|
+
|
|
4
|
+
## Description
|
|
5
|
+
In the UECA-React framework, `onPropChange` and `onPropChanging` are embedded events that automatically fire whenever a property in a component's model changes. Unlike the property-specific `onChange<Prop>` and `onChanging<Prop>` events, these events are designed to handle changes across **all properties** using a single handler. They include an additional `prop` parameter, which specifies the name of the property being modified, enabling centralized logic for multiple properties.
|
|
6
|
+
|
|
7
|
+
Key features:
|
|
8
|
+
- **Automatic Generation**: Event handlers are created without explicit declaration in component structure.
|
|
9
|
+
- **Automatic Execution**: Triggered for every property update in the model.
|
|
10
|
+
- **Centralized Management**: One event handler can manage all property changes.
|
|
11
|
+
- **Pre- and Post-Change Hooks**: `onPropChanging` runs before the update, while `onPropChange` runs after.
|
|
12
|
+
- **Flexible Use Cases**: Ideal for validation, logging, or uniform transformations.
|
|
13
|
+
|
|
14
|
+
These events simplify state management in complex components by reducing the need for multiple property-specific handlers.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## API/Methods
|
|
19
|
+
|
|
20
|
+
### Event Handlers
|
|
21
|
+
- **onPropChanging**
|
|
22
|
+
Fired **before** a property is updated.
|
|
23
|
+
- **Signature**: `(prop: string, newValue: unknown, oldValue: unknown) => unknown`
|
|
24
|
+
- `prop`: The name of the changing property.
|
|
25
|
+
- `newValue`: The proposed new value.
|
|
26
|
+
- `oldValue`: The current value before the change.
|
|
27
|
+
- **Return**:
|
|
28
|
+
- A value to apply.
|
|
29
|
+
- `oldValue` to block the change.
|
|
30
|
+
- **Purpose**: Validate or transform values before they are set.
|
|
31
|
+
|
|
32
|
+
- **onPropChange**
|
|
33
|
+
Fired **after** a property is updated.
|
|
34
|
+
- **Signature**: `(prop: string, newValue: unknown, oldValue: unknown) => void`
|
|
35
|
+
- `prop`: The name of the changed property.
|
|
36
|
+
- `newValue`: The value after the update.
|
|
37
|
+
- `oldValue`: The value before the update.
|
|
38
|
+
- **Purpose**: React to changes (e.g., logging or side effects).
|
|
39
|
+
|
|
40
|
+
### Usage
|
|
41
|
+
- Define these events in the `events` section of a UECA-React component structure.
|
|
42
|
+
- They are triggered automatically by property assignments (e.g., `model.firstName = "John"`).
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Code Example
|
|
47
|
+
Here’s an example demonstrating how to use `onPropChanging` for validation and `onPropChange` for logging:
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import * as UECA from "ueca-react";
|
|
51
|
+
|
|
52
|
+
type DemoStruct = UECA.ComponentStruct<{
|
|
53
|
+
props: {
|
|
54
|
+
age: number;
|
|
55
|
+
username: string
|
|
56
|
+
};
|
|
57
|
+
}>;
|
|
58
|
+
|
|
59
|
+
type DemoParams = UECA.ComponentParams<DemoStruct>;
|
|
60
|
+
type DemoModel = UECA.ComponentModel<DemoStruct>;
|
|
61
|
+
|
|
62
|
+
function useDemo(params?: DemoParams): DemoModel {
|
|
63
|
+
const struct: DemoStruct = {
|
|
64
|
+
props: {
|
|
65
|
+
id: useDemo.name,
|
|
66
|
+
age: 25,
|
|
67
|
+
username: "user"
|
|
68
|
+
},
|
|
69
|
+
events: {
|
|
70
|
+
onPropChanging: (prop, newValue, oldValue) => {
|
|
71
|
+
console.log(`Attempting to change ${prop} from ${oldValue} to ${newValue}`);
|
|
72
|
+
if (prop === "age" && typeof newValue === "number" && newValue < 0) {
|
|
73
|
+
return oldValue; // Prevent negative age
|
|
74
|
+
}
|
|
75
|
+
return newValue;
|
|
76
|
+
},
|
|
77
|
+
onPropChange: (prop, newValue, oldValue) => {
|
|
78
|
+
console.log(`${prop} updated from ${oldValue} to ${newValue}`);
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
View: () => (
|
|
82
|
+
<div id={model.htmlId()}>
|
|
83
|
+
<p>Age: {model.age}</p>
|
|
84
|
+
<p>Username: {model.username}</p>
|
|
85
|
+
<button onClick={() => model.age = model.age - 10}>Decrease Age</button>
|
|
86
|
+
<button onClick={() => model.username = "newUser"}>Change Username</button>
|
|
87
|
+
</div>
|
|
88
|
+
),
|
|
89
|
+
};
|
|
90
|
+
const model = UECA.useComponent(struct, params);
|
|
91
|
+
return model;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const Demo = UECA.getFC(useDemo);
|
|
95
|
+
export { DemoModel, useDemo, Demo };
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
- **Explanation**:
|
|
99
|
+
- `onPropChanging` checks if `age` is becoming negative and blocks it by returning `oldValue`.
|
|
100
|
+
- `onPropChange` logs every successful change for any property.
|
|
101
|
+
- One handler manages both `age` and `username` updates.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Benefits
|
|
106
|
+
- **Reduced Code Duplication**: No need for separate handlers per property.
|
|
107
|
+
- **Dynamic Handling**: The `prop` parameter allows conditional logic based on property names.
|
|
108
|
+
- **Control**: Modify or block changes with `onPropChanging`.
|
|
109
|
+
|
|
110
|
+
## Notes
|
|
111
|
+
- These events complement, rather than replace, `onChange<Prop>` and `onChanging<Prop>`. Use the latter for property-specific logic.
|
|
112
|
+
- Ensure handlers are lightweight to maintain performance, as they execute for every property change.
|