Skip to content

Commit ab6eba8

Browse files
committed
Docs (RxProps): Updates the section according to the new API
1 parent 1740496 commit ab6eba8

File tree

1 file changed

+94
-45
lines changed

1 file changed

+94
-45
lines changed

docs/architecture/reactive-props.md

Lines changed: 94 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,68 @@
11
# Reactive props
2-
3-
> This is a highly experimental technology and it may be changed, or removed in the future releases.
4-
52
* [Specification](#specification)
6-
* [Declaration](#declaration)
7-
* [Delegated subscription](#delegated-subscription)
3+
* [Reactive field props](#reactive-field-props)
4+
* [Reactive validation rules](#reactive-validation-rules)
85

96
## Specification
10-
*Reactive prop* - is a field's prop, which value is resolved automatically using the live subscriptions system. The latter allows to subscribe to the specific props changes of the fields referenced within the reactive prop resolver function, and re-resolve it anytime the props change.
7+
> This is a highly experimental technology and it may change, or be removed in the future. Follow the release notes for more information.
8+
9+
*Reactive prop* - is a field's prop, which value is resolved automatically using the live subscriptions system. The latter allows to subscribe to props changes of another fields, and re-evaluate the reactive prop's resolver whenever the referenced props update.
1110

12-
> **Note:** Right now only `required` prop can be used as a reactive prop.
11+
This is a generic concept implemented in several features of React Advanced Form. Those are listed below in this section. Each feature may utilize this concept in a different way, although the interface strives to be unified.
1312

14-
## Declaration
15-
Whenever another field's prop is referenced using the exposed `fields` argument property, a live subscription for that referenced prop is created.
13+
## Behavior
14+
Below, there is a list of behaviors applicable to reactive props, regardless of where they are used. For consistency's sake, [Reactive field props](#reactive-field-props) interface is used in these examples.
15+
16+
### Safe key reference
17+
There is no need to explicitly check for the field's existence in order to reference props of potentially non-existent fields. The interface of reactive props provides safe key reference using Immutable instances.
18+
19+
For example, this is the ***wrong*** way of declaring a prop subscription:
1620

1721
```jsx
18-
<Input
19-
name="fieldOne"
20-
initialValue="foo" />
21-
<Input
22-
name="fieldTwo"
23-
required={({ fields }) => {
24-
return !!fields.fieldOne.value;
22+
<Checkbox
23+
required={({ get, fields }) => {
24+
// the WRONG way of reactive prop declaration
25+
const foo = get(['fieldOne']) && get(['fieldOne', 'value']);
26+
const bar = fields.fieldTwo && get(['fieldTwo', 'value']);
27+
return foo && bar;
2528
}} />
2629
```
2730

28-
Notice how `fieldTwo.required` equals a function which references the `fieldOne` field, and its `value` prop. Because of that, the `required` prop becomes reactive, re-updating each time `fieldOne.valuve` changes.
29-
30-
Reactive prop resolver is kept in the dedicated key of the [Field record](./field-lifecycle.md#field-record), while the reactive prop's value always equals the result of the executed resolver. For example, the value of `fieldTwo.required` would equal to `true` at the initial mount, since the resolver function returns `true`.
31+
The ***correct*** way would be much more clean and simple:
3132

32-
> **Note:** It is possible to reference any props present in the [Field's state](../hoc/createField/props.md#field-state), which include `value`, `valid`, `validSync`, and many more.
33-
34-
### Safe key path reference
35-
React Advanced Form uses a recursive Objecy proxy to analyze the referenced fields, therefore, it's **not needed** to ensure the safe key path reference, even if referencing deep non-existing keys. The latter are automatically ensafed, allowing you to write clear fields references.
33+
```jsx
34+
<Checkbox
35+
required={({ get }) => {
36+
/* "get" checks if "fieldOne" exists automatically */
37+
const foo = get(['fieldOne', 'value']);
38+
const bar = get(['fieldTwo', 'required']);
3639

37-
That being said, the following key path reference **will not throw**:
38-
```js
39-
fields.nonExistingGroup.nonExistingField.propName;
40+
return foo && bar;
41+
}}>
4042
```
4143

42-
### Multiple fields references
43-
It is perfectly fine to reference multiple fields within the reactive prop's resolver function:
44+
In case the referenced field, or its prop, doesn't exist, the getter will return `undefined`.
45+
46+
### Multiple field references
47+
It is possible to reference multiple fields within a single reactive prop resolver function:
4448

4549
```jsx
4650
<Checkbox
4751
name="termsAndConditions"
48-
required={({ fields }) => {
49-
const hasFirstName = fields.firstName.valid;
50-
const hasLastName = fields.lastName.valid;
52+
required={({ get }) => {
53+
const foo = get(['firstName', 'valid']);
54+
const bar = get(['lastName', 'required']);
5155

52-
return hasFirstName && hasLastName;
56+
return foo && !bar;
5357
}} />
5458
```
5559

56-
## Delegated subscription
57-
Reactive prop resolver can also reference the fields which haven't mounted yet at the moment of its declaration. In that case a direct subscription cannot be created.
60+
This will create two observers, respectively, and each change of the referenced props will trigger re-evaluataion of that `required` resolver function.
61+
62+
### Delegated subscription
63+
Reactive prop resolver can also reference fields which haven't mounted yet at the moment when the resolver function is declared. In that case a direct subscription cannot be created. Instead, a *delegated* subscription is created.
5864

59-
Nevertheless, React Advanced Form knows which fields have been referenced, regardless of the mounting status of the latter. In case the referenced field is not mounted, a *delegated subscription* is created.
65+
The purpose of the delegated subscription is to listen for the field registration event and re-evaluate the resolver function after the mounting occurs.
6066

6167
Delegated subscription behaves the following way:
6268

@@ -67,23 +73,66 @@ Delegated subscription behaves the following way:
6773

6874
To illustrate this, consider the next scenario:
6975

76+
```jsx
77+
<Input
78+
name="fieldTwo"
79+
required={({ get }) => !!get(['fieldThree', 'value'])} />
80+
<Input
81+
name="fieldThree"
82+
initialValue="doe" />
83+
```
84+
85+
By the time `fielTwo.props.required` reactive prop is evaluated, the referenced `fieldThree` doesn't exist yet. Once the referenced field has been mounted, the resolver function (`fieldTwo.props.required`) is re-evaluated, and the value of `required` prop is updated according to the resolver logic (to `true` in the example above).
86+
87+
## Reactive field props
88+
To create a reactive field prop simply pass a function as its value, and use the exposed `get` method to reference props of another fields.
89+
90+
> **Note:** At the moment reactive field props are supported only for the `required` prop.
91+
92+
### Syntax
93+
94+
```jsx
95+
<FieldComponent
96+
required={({ get }) => {
97+
return get(['path', 'to', 'field"s', 'prop']);
98+
}} />
99+
```
100+
101+
### Usage
102+
Whenever another field's prop is referenced using the `get` method, a live subscription for that referenced prop is created.
103+
70104
```jsx
71105
<Input
72106
name="fieldOne"
73107
initialValue="foo" />
74-
75108
<Input
76109
name="fieldTwo"
77-
required={({ fields }) => {
78-
const fieldOneFilled = !!fields.fieldOne.value;
79-
const fieldThreeFilled = !!fields.fieldThree.value;
110+
required={({ get }) => !!get(['fieldOne', 'value'])} />
111+
```
80112

81-
return fieldOneFilled && fieldThreeFilled;
82-
}} />
113+
Reactive props resolver is never assigned as a value of the reactive prop. Instead, it is copied and executed whenever the referenced prop updates, consequentially updating the prop, which is connected to the resolver.
83114

84-
<Input
85-
name="fieldThree"
86-
initialValue="doe" />
115+
> **Note:** It is possible to reference any props from the [Field's state](../hoc/createField/props.md#field-state) (i.e. `value`, `valid`, `validSync`, and many more).
116+
117+
---
118+
119+
## Reactive validation rules
120+
The concept of reactive props is applicable to synchronous validation rules as well. Just as with the [Reactive field props](#reactive-field-props), whenever another field's prop is referenced within a synchronous validation rule declaration, the latter is re-evaluated each time the referenced prop updates.
121+
122+
### Syntax
123+
```js
124+
export default {
125+
name: {
126+
confirmPassword: ({ value, get }) => {
127+
/**
128+
* This reads as:
129+
* The "confirmPassword" field is valid only when its value
130+
* equals to "userPassword" field "value" prop.
131+
*/
132+
return value === get(['userPassword', 'value']);
133+
}
134+
}
135+
};
87136
```
88137

89-
`fieldTwo` has the reactive prop `required`, which depends on `fieldOne.value` and `fieldThree.value`. By the time the resolver is created, `fieldThree` is not mounted in the form. For that the delegated subscription is created, awaiting for the `fieldThree` to register (which, essentially, means to mount). Once the referenced fiels has been mounted, the value of `fieldTwo.required` is re-resolved into `true`, since both referenced fields have their values set.
138+
> The syntax for type-specific validation rules is identical.

0 commit comments

Comments
 (0)