Skip to content

Commit 45d5554

Browse files
committed
fix: fix setValue bug
1 parent 1b7f4e7 commit 45d5554

22 files changed

+304
-358
lines changed

docs/demos/_controller.tsx

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,38 @@ import React, { useState } from 'react';
22
import { useForm } from 'react-form-simple';
33

44
export default function App() {
5-
const { render, model } = useForm({ name: 'name' });
5+
const { render, model, setValues, setValue } = useForm({
6+
name: 'name',
7+
arr: [{ name: 1 }],
8+
originData: { name: 2 },
9+
});
610

711
const [modelState, setModelState] = useState('');
812

913
const renderName = render('name')(<input />);
1014

11-
const onSubmit = () => setModelState(JSON.stringify(model));
15+
const onSubmit = () => {
16+
setValues({ arr: [{ name: 222 }], originData: { name: 33 } });
17+
return;
18+
const datas = {
19+
name: 'name111',
20+
arr: [{ name: 2 }],
21+
};
22+
setValues({
23+
...datas,
24+
originData: {
25+
...datas,
26+
name: 'originName',
27+
},
28+
});
29+
console.log('model', model);
30+
};
1231

1332
return (
1433
<>
1534
{renderName}
1635
{modelState}
36+
{render('originData.name')(<input />)}
1737
<button onClick={onSubmit}>submit</button>
1838
</>
1939
);

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@
6868
"dependencies": {
6969
"@emotion/css": "^11.11.2",
7070
"lodash": "^4.17.21",
71-
"proxy-polyfill": "^0.3.2",
7271
"react-transition-group": "^4.4.5"
7372
},
7473
"devDependencies": {

src/__tests__/form.test.tsx

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@ describe('form', () => {
4747

4848
describe.concurrent('form item', () => {
4949
test('contextProps', async ({ expect }) => {
50-
let apiEffectOptionss = null;
50+
let apiEffectOptions = null;
5151
const contextProps: FormItemProps['contextProps'] = {
5252
destroy() {},
5353
apiEffect(args) {
54-
apiEffectOptionss = args;
54+
apiEffectOptions = args;
5555
},
5656
mounted() {},
5757
};
@@ -88,9 +88,9 @@ describe.concurrent('form item', () => {
8888

8989
expect(mockApiEffectFn).toHaveBeenCalled();
9090

91-
expect(apiEffectOptionss).not.toBeNull();
91+
expect(apiEffectOptions).not.toBeNull();
9292

93-
expect(apiEffectOptionss).toBeTypeOf('object');
93+
expect(apiEffectOptions).toBeTypeOf('object');
9494

9595
expect(mockMounter).toHaveBeenCalled();
9696

@@ -224,7 +224,7 @@ describe.concurrent('form item', () => {
224224
});
225225
// test('form item vaild test', async () => {
226226
// const onError = vi.fn();
227-
// const comppnentRef = React.createRef();
227+
// const componentRef = React.createRef();
228228
// const TestDemo = React.forwardRef<any, any>((props, ref) => {
229229
// return (
230230
// <FormItem
@@ -237,15 +237,15 @@ describe.concurrent('form item', () => {
237237
// />
238238
// );
239239
// });
240-
// const { unmount } = testRender(<TestDemo ref={comppnentRef} />);
241-
// (comppnentRef.current as any).validate();
240+
// const { unmount } = testRender(<TestDemo ref={componentRef} />);
241+
// (componentRef.current as any).validate();
242242
// await expect(onError).toHaveBeenCalled();
243243
// unmount();
244244
// });
245245
});
246246

247247
describe.concurrent('use Form Item api', () => {
248-
test('clear vaild', async ({ expect }) => {
248+
test('clear valid', async ({ expect }) => {
249249
const TestDemo = () => {
250250
const { render, clearValidate } = useForm({ clearName: 'default value' });
251251
useEffect(() => {
@@ -262,11 +262,11 @@ describe.concurrent('use Form Item api', () => {
262262

263263
fireEvent.change(input, { target: { value: '' } });
264264
const getErrorText = () => {
265-
const componet = container.querySelector(
265+
const component = container.querySelector(
266266
'[data-error-id="clearName-PleaseInput"]',
267267
) as HTMLElement;
268-
if (componet) {
269-
return componet.getAttribute('data-error-text');
268+
if (component) {
269+
return component.getAttribute('data-error-text');
270270
}
271271
return null;
272272
};
@@ -290,14 +290,14 @@ describe.concurrent('use Form Item api', () => {
290290
});
291291
test('reset', async ({ expect }) => {
292292
const TestDemo = () => {
293-
const { render, reset, model } = useForm({ restVaild: '' });
293+
const { render, reset, model } = useForm({ restValid: '' });
294294

295295
useEffect(() => {
296-
model.restVaild = 'test value';
296+
model.restValid = 'test value';
297297
reset();
298298
}, []);
299299

300-
return <>{render('restVaild')(<input id="reset-value-test" />)}</>;
300+
return <>{render('restValid')(<input id="reset-value-test" />)}</>;
301301
};
302302
const { container, unmount } = testRender(<TestDemo />);
303303

@@ -319,15 +319,15 @@ describe.concurrent('use Form Item api', () => {
319319
expect(getInputValue()).toBe('');
320320
unmount();
321321
});
322-
test('remove and reapply vaild', async ({ expect }) => {
323-
const comppnentRef = React.createRef();
322+
test('remove and reapply valid', async ({ expect }) => {
323+
const componentRef = React.createRef();
324324
const TestDemo = React.forwardRef((props, ref) => {
325325
const { render, removeValidator, reapplyValidator, validate } = useForm({
326326
removeName: 'test value',
327327
});
328328

329329
useImperativeHandle(ref, () => ({
330-
vaild() {
330+
validate() {
331331
return validate();
332332
},
333333
}));
@@ -345,7 +345,7 @@ describe.concurrent('use Form Item api', () => {
345345
click
346346
</button>
347347
);
348-
const buttonVaild = (
348+
const buttonValid = (
349349
<button
350350
type="button"
351351
id="valid-button"
@@ -357,15 +357,15 @@ describe.concurrent('use Form Item api', () => {
357357
return (
358358
<>
359359
{button}
360-
{buttonVaild}
360+
{buttonValid}
361361
{render('removeName', { rules: { required: 'Please Input' } })(
362362
<input id="remove-input" />,
363363
)}
364364
</>
365365
);
366366
});
367367

368-
const { container, unmount } = testRender(<TestDemo ref={comppnentRef} />);
368+
const { container, unmount } = testRender(<TestDemo ref={componentRef} />);
369369

370370
const input = container.querySelector('#remove-input') as HTMLInputElement;
371371
await vi.waitFor(() => Promise.resolve(), { timeout: 1000, interval: 100 });
@@ -380,7 +380,7 @@ describe.concurrent('use Form Item api', () => {
380380
fireEvent.click(button);
381381

382382
await expect(() =>
383-
(comppnentRef.current as any).vaild(),
383+
(componentRef.current as any).validate(),
384384
).rejects.toThrowError('Please Input');
385385

386386
unmount();

src/driver/ControllerDriver/controller.ts

Lines changed: 41 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,17 @@
1-
import { cloneDeep } from 'lodash';
2-
import ProxyPolyfillBuilder from 'proxy-polyfill/src/proxy';
31
import React from 'react';
42
import type { DefaultRecord } from 'react-form-simple';
5-
import { isObject, isObjectOrArray } from 'react-form-simple/utils/util';
6-
7-
const ProxyPolyfill = window.Proxy || ProxyPolyfillBuilder();
3+
import { isObjectOrArray } from 'react-form-simple/utils/util';
84

95
export type ObserverOptions = {
106
path?: string[];
11-
onChangeLength?: () => void;
127
};
138

149
export type ObserverCb = { path: string; value: any };
1510

16-
export const toTarget = (proxy: any) => cloneDeep(proxy);
17-
18-
interface ProxyObject {
19-
[key: string]: any;
20-
}
21-
22-
const isSkippableType = (value: any) =>
23-
value instanceof Date || value instanceof Blob || value instanceof File;
24-
2511
export const replaceTarget = (proxyObject: any, values: any) => {
26-
if (!isObject(values)) return proxyObject;
27-
function setNestedValue(obj: ProxyObject, keys: string[], value: any): void {
28-
const lastKey = keys.pop();
29-
let currentObj = obj;
30-
31-
keys.forEach((key) => {
32-
if (!currentObj[key] || typeof currentObj[key] !== 'object') {
33-
currentObj[key] = {};
34-
}
35-
currentObj = currentObj[key];
36-
});
37-
38-
currentObj[lastKey as string] = value;
39-
}
40-
41-
function processArray(obj: ProxyObject, value: any[], path: string[]): void {
42-
obj[path[0]] = value.map((item) => {
43-
if (typeof item === 'object' && item !== null && !Array.isArray(item)) {
44-
const nestedObj: ProxyObject = {};
45-
processValues(nestedObj, item);
46-
return nestedObj;
47-
}
48-
return item;
49-
});
50-
}
51-
52-
function processValues(
53-
obj: ProxyObject,
54-
values: Record<string, any>,
55-
currentPath: string[] = [],
56-
): void {
57-
for (const [key, value] of Object.entries(values)) {
58-
const path = [...currentPath, key];
59-
60-
if (Array.isArray(value)) {
61-
processArray(obj, value, path);
62-
} else if (typeof value === 'object' && value !== null) {
63-
if (isSkippableType(value)) {
64-
setNestedValue(obj, path, value);
65-
} else {
66-
if (Object.keys(value).length === 0) {
67-
setNestedValue(obj, path, { ...value });
68-
} else {
69-
processValues(obj, value, path);
70-
}
71-
}
72-
} else {
73-
setNestedValue(obj, path, value);
74-
}
75-
}
76-
}
77-
78-
processValues(proxyObject, values);
12+
Object.entries(values).forEach(([key, value]) => {
13+
updateProxyValue(proxyObject, key, value);
14+
});
7915

8016
return proxyObject;
8117
};
@@ -92,6 +28,7 @@ export const getProxyValue = (
9228
if (currentObj?.[currentKey] === undefined) {
9329
return undefined; // Key path doesn't exist in the object
9430
}
31+
9532
currentObj = currentObj[currentKey];
9633
}
9734

@@ -100,37 +37,57 @@ export const getProxyValue = (
10037

10138
export const updateProxyValue = (
10239
obj: any,
103-
key: string | number | undefined | null | symbol,
40+
path: string,
10441
newValue: any,
105-
) => {
106-
if (!obj || !key) return;
107-
const keys = String(key).split('.');
108-
let currentObj = obj;
42+
options: {
43+
createPath?: boolean;
44+
forceUpdate?: boolean;
45+
} = {},
46+
): boolean => {
47+
if (!obj || !path) return false;
48+
49+
const keys = path.split('.');
50+
let current = obj;
51+
52+
for (let i = 0; i < keys.length - 1; i++) {
53+
const key = keys[i];
54+
55+
if (current[key] === undefined) {
56+
if (options.createPath) {
57+
current[key] = {};
58+
} else {
59+
return false;
60+
}
61+
}
10962

110-
for (const currentKey of keys.slice(0, -1)) {
111-
if (
112-
currentObj?.[currentKey] === undefined ||
113-
typeof currentObj[currentKey] !== 'object'
114-
) {
115-
break;
63+
if (typeof current[key] !== 'object' || current[key] === null) {
64+
if (options.createPath) {
65+
current[key] = {};
66+
} else {
67+
return false;
68+
}
11669
}
117-
currentObj = currentObj[currentKey];
70+
71+
current = current[key];
11872
}
11973

12074
const lastKey = keys[keys.length - 1];
121-
if (lastKey in currentObj) {
122-
currentObj[lastKey] = newValue;
75+
if (options.forceUpdate || lastKey in current) {
76+
current[lastKey] = newValue;
77+
return true;
12378
}
79+
80+
return false;
12481
};
12582

12683
export const observer = <T extends DefaultRecord>(
12784
initialVal: T,
12885
cb?: (args: ObserverCb) => void,
12986
options?: ObserverOptions,
13087
): T => {
131-
const { path = [], onChangeLength } = (options || {}) as ObserverOptions;
88+
const { path = [] } = (options || {}) as ObserverOptions;
13289

133-
const proxy = new ProxyPolyfill(initialVal, {
90+
const proxy = new Proxy(initialVal, {
13491
get(target, key, receiver) {
13592
const ret = Reflect.get(target, key, receiver);
13693
if (React.isValidElement(ret)) return ret;
@@ -148,14 +105,6 @@ export const observer = <T extends DefaultRecord>(
148105

149106
cb?.({ path: newPath.join('.'), value: val });
150107

151-
if (
152-
Array.isArray(target) &&
153-
key === 'length' &&
154-
typeof onChangeLength === 'function'
155-
) {
156-
onChangeLength();
157-
}
158-
159108
return ret;
160109
},
161110
});

src/driver/ObserverDriver/implementation/Watch.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import {
66
CallbackType,
77
KeyType,
88
RequiredContextType,
9-
SubscripeFunType,
9+
SubscribeFunType,
1010
} from '../type';
1111

1212
export class Watch<T> extends AbstractImp {
1313
private cb: CallbackType = () => {};
14-
private preRet: UseWatchNamespace.SubscripeFunReturnType = null;
15-
private subscribeFun: SubscripeFunType<T> = () => ({});
14+
private preRet: UseWatchNamespace.SubscribeFunReturnType = null;
15+
private subscribeFun: SubscribeFunType<T> = () => ({});
1616
constructor(
1717
public key: KeyType,
1818
public contextProps: RequiredContextType<T>,
@@ -41,7 +41,7 @@ export class Watch<T> extends AbstractImp {
4141
}
4242
this.preRet = cloneDeep(ret);
4343
}
44-
public update(subscribeFun: SubscripeFunType<T>, cb: CallbackType) {
44+
public update(subscribeFun: SubscribeFunType<T>, cb: CallbackType) {
4545
this.cb = cb;
4646
this.subscribeFun = subscribeFun;
4747
}

0 commit comments

Comments
 (0)