Skip to content

Commit 28e3b35

Browse files
committed
Uses field prop getter function instead of Proxy
1 parent 5eff10f commit 28e3b35

File tree

9 files changed

+59
-64
lines changed

9 files changed

+59
-64
lines changed

examples/reactive-props/DelegatedSubscription.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ export default class RxPropsDelegatedSubscription extends React.Component {
1414
name="firstName"
1515
label="Fisrt name"
1616
hint="Required when `lastName` has value"
17-
required={({ fields }) => {
18-
return !!fields.lastName.value;
17+
required={({ getFieldProp }) => {
18+
return !!getFieldProp(['lastName', 'value']);
1919
}} />
2020
<Input
2121
name="lastName"

examples/reactive-props/DynamicRequired.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ export default class RxPropsDynamicRequired extends React.Component {
1818
name="lastName"
1919
label="Last name"
2020
hint="Required when `firstName` has value"
21-
required={({ fields }) => {
22-
return !!fields.firstName.value;
21+
required={({ getFieldProp }) => {
22+
return !!getFieldProp(['firstName', 'value']);
2323
}} />
2424

2525
<Button>Submit</Button>

examples/reactive-props/Interdependent.jsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ export default class RxPropsInterdependent extends React.Component {
1414
name="firstName"
1515
label="First name"
1616
hint="Required when `lastName` has value"
17-
required={({ fields }) => {
18-
return !!fields.lastName.value;
17+
required={({ getFieldProp }) => {
18+
return !!getFieldProp(['lastName', 'value']);
1919
}} />
2020
<Input
2121
name="lastName"
2222
label="Last name"
2323
hint="Required when `firstName` has value"
24-
required={({ fields }) => {
25-
return !!fields.firstName.value;
24+
required={({ getFieldProp }) => {
25+
return !!getFieldProp(['firstName', 'value']);
2626
}} />
2727

2828
<Button>Submit</Button>

examples/reactive-props/SingleTarget.jsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ export default class RxPropsSingleTarget extends React.Component {
1414
name="firstName"
1515
label="First name"
1616
hint="Required when `lastName` has value"
17-
required={({ fields }) => {
18-
return !!fields.lastName.value;
17+
required={({ getFieldProp }) => {
18+
return !!getFieldProp(['lastName', 'value']);
1919
}} />
2020
<Input
2121
name="lastName"
@@ -24,8 +24,8 @@ export default class RxPropsSingleTarget extends React.Component {
2424
name="fieldThree"
2525
label="Some field three"
2626
hint="Required when `lastName` has value"
27-
required={({ fields }) => {
28-
return !!fields.lastName.value;
27+
required={({ getFieldProp }) => {
28+
return !!getFieldProp(['lastName', 'value'])
2929
}} />
3030
<Button>Submit</Button>
3131
</Form>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* Generates a field prop getter function.
3+
* The latter is used for reactive props implementation and allows to flush
4+
* field prop references into a single source using a callback function.
5+
* @param {Map} fields
6+
* @param {Function} callback
7+
*/
8+
export default function createPropGetter(fields, callback) {
9+
return (propPath) => {
10+
const propValue = fields.getIn(propPath);
11+
12+
if (callback) {
13+
callback(propPath, propValue);
14+
}
15+
16+
return propValue;
17+
};
18+
}

src/utils/fieldUtils/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ export getErrorMessages from './getErrorMessages';
1010

1111
/* Other */
1212
export serializeFields from './serializeFields';
13+
export createPropGetter from './createPropGetter';

src/utils/flushFieldRefs.js

Lines changed: 11 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,21 @@
1-
import ensafeMap from './ensafeMap';
21
import dispatch from './dispatch';
2+
import createPropGetter from './fieldUtils/createPropGetter';
33

44
/**
5-
* Recursively proxies the given origin. Calls optional "callback" function whenever any property
6-
* if the origin is being referenced.
7-
* @param {Object} origin
8-
* @param {Function} callback
9-
* @returns {Proxy}
10-
*/
11-
function proxyWithCallback(origin, callback) {
12-
return new Proxy(origin, {
13-
get(target, propName) {
14-
if (callback) callback({ target, propName });
15-
return proxyWithCallback({}, callback);
16-
}
17-
});
18-
}
19-
20-
/**
21-
* Returns the collection of field paths of the fields referenced within the provided method.
5+
* Returns the map of flushed field props paths referenced within the provided
6+
* method, and its initial value.
227
* @param {Function} method
23-
* @param {Object} fields
8+
* @param {CallbackHandlerArgs} methodArgs
249
*/
25-
export default function flushFieldRefs(method, { fields, ...restParams }) {
26-
const { form } = restParams;
10+
export default function flushFieldRefs(method, methodArgs) {
11+
const { fields, form } = methodArgs;
2712
const refs = [];
28-
const mutableFields = fields.toJS();
29-
30-
/* Assign a temporary property to state the root level of target Object */
31-
mutableFields.__IS_ROOT__ = true;
32-
33-
const fieldsProxy = proxyWithCallback(mutableFields, ({ target, propName }) => {
34-
if (target.__IS_ROOT__) {
35-
refs.push([]);
36-
}
37-
38-
const refEntry = refs[refs.length - 1];
39-
refEntry.push(propName);
40-
});
41-
42-
/* First, execute the method with proxied fields to gather the field path refs */
43-
dispatch(method, restParams, form.context, { fields: fieldsProxy });
13+
const getFieldProp = createPropGetter(fields, propPath => refs.push(propPath));
4414

45-
/* Second, execute the method with missing path refs set to "undefined" to prevent throwing */
46-
const safeFields = ensafeMap(fields, refs);
47-
const initialValue = dispatch(method, { ...restParams, fields: safeFields }, form.context);
15+
const initialValue = dispatch(method, {
16+
...methodArgs,
17+
getFieldProp
18+
}, form.context);
4819

4920
return { refs, initialValue };
5021
}

src/utils/rxUtils/createSubscriptions.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import makeObservable from './makeObservable';
2-
import ensafeMap from '../ensafeMap';
2+
import createPropGetter from '../fieldUtils/createPropGetter';
33

44
/**
55
* @param {Map} fieldProps
@@ -14,16 +14,16 @@ export default function createSubscriptions({ fieldProps, fields, form }) {
1414
const resolverArgs = { fieldProps, fields, form };
1515

1616
rxProps.forEach((resolver, rxPropName) => {
17-
const { refs } = makeObservable(resolver, resolverArgs, {
17+
makeObservable(resolver, resolverArgs, {
1818
initialCall: true,
1919
subscribe({ nextContextProps, shouldValidate = true }) {
2020
const refFieldPath = nextContextProps.get('fieldPath');
2121
const nextFields = form.state.fields.set(refFieldPath, nextContextProps);
22-
const safeFields = ensafeMap(nextFields, refs);
2322

2423
const nextPropValue = resolver({
2524
fieldProps: fieldProps.toJS(),
26-
fields: safeFields.toJS(),
25+
fields: nextFields.toJS(),
26+
getFieldProp: createPropGetter(nextFields),
2727
form
2828
}, form.context);
2929

test/unit/utils/flushFieldRefs.spec.js

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
import { fromJS } from 'immutable';
22
import { expect } from 'chai';
33
import { form } from '../../utils';
4-
import { flushFieldRefs } from '../../../src/utils';
4+
import { flushFieldRefs, fieldUtils } from '../../../src/utils';
55

66
describe('flushFieldRefs', function () {
77
it('Flushes referenced field paths properly', () => {
88
const fields = fromJS({ fieldOne: { value: 'foo' } });
9-
const method = ({ fields }) => {
10-
fields.fieldOne.value;
11-
fields.groupOne.fieldOne.required;
9+
const method = ({ getFieldProp }) => {
10+
getFieldProp(['fieldOne', 'value']);
11+
getFieldProp(['groupOne', 'fieldOne', 'required']);
1212
};
1313

14-
const { refs } = flushFieldRefs(method, { fields, form });
14+
const getFieldProp = fieldUtils.createPropGetter(fields);
15+
const { refs } = flushFieldRefs(method, {
16+
getFieldProp,
17+
fields,
18+
form
19+
});
1520

1621
expect(refs).to.be.an.instanceof(Array).with.lengthOf(2);
1722
expect(refs[0]).to.deep.equal(['fieldOne', 'value']);
@@ -20,9 +25,9 @@ describe('flushFieldRefs', function () {
2025

2126
it('Resolves initial value properly', () => {
2227
const fields = fromJS({ fieldOne: { value: 'foo' } });
23-
const method = ({ fields }) => {
24-
fields.nonExisting.fieldPath.propName;
25-
return fields.fieldOne.value;
28+
const method = ({ getFieldProp }) => {
29+
getFieldProp(['nonExisting', 'fieldPath', 'propName']);
30+
return getFieldProp(['fieldOne', 'value']);
2631
};
2732

2833
const { initialValue } = flushFieldRefs(method, { fields, form });

0 commit comments

Comments
 (0)