Skip to content

Commit a5fb1ac

Browse files
committed
fix(@uform/core): fix alibaba#613 and alibaba#615
1 parent 01c6c76 commit a5fb1ac

File tree

15 files changed

+289
-53
lines changed

15 files changed

+289
-53
lines changed

packages/core/src/__tests__/__snapshots__/index.spec.ts.snap

+109
Large diffs are not rendered by default.

packages/core/src/__tests__/index.spec.ts

+36-9
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,33 @@ describe('createForm', () => {
111111
expect(form.getFormGraph()).toMatchSnapshot()
112112
})
113113

114+
const sleep = (d=1000)=>new Promise((resolve)=>{
115+
setTimeout(()=>{
116+
resolve()
117+
},d)
118+
})
119+
120+
test('invalid initialValue will not trigger validate', async () => {
121+
const form = createForm()
122+
const field = form.registerField({
123+
name: 'aa',
124+
rules:[{
125+
required:true
126+
}]
127+
})
128+
const mutators = form.createMutators(field)
129+
field.subscribe(() => {
130+
mutators.validate({ throwErrors: false })
131+
})
132+
form.setFormState(state => {
133+
state.initialValues = {
134+
aa: null
135+
}
136+
})
137+
await sleep(10)
138+
expect(field.getState(state=>state.errors).length).toEqual(1)
139+
})
140+
114141
test('lifecycles', () => {
115142
const onFormInit = jest.fn()
116143
const onFieldInit = jest.fn()
@@ -437,9 +464,9 @@ describe('clearErrors', () => {
437464
expect(form.getFormState(state => state.errors)).toEqual([])
438465
})
439466

440-
test('wildcard path', async () => { })
467+
test('wildcard path', async () => {})
441468

442-
test('effect', async () => { })
469+
test('effect', async () => {})
443470
})
444471

445472
describe('validate', () => {
@@ -487,7 +514,7 @@ describe('validate', () => {
487514

488515
try {
489516
await form.submit()
490-
} catch (e) { }
517+
} catch (e) {}
491518
expect(onValidateFailedTrigger).toBeCalledTimes(1)
492519
})
493520

@@ -515,7 +542,7 @@ describe('validate', () => {
515542
}) // CustomValidator error
516543
try {
517544
await form.submit()
518-
} catch (e) { }
545+
} catch (e) {}
519546
expect(onValidateFailedTrigger).toBeCalledTimes(1)
520547
})
521548

@@ -1560,16 +1587,16 @@ describe('major sences', () => {
15601587
expect(form.getFormGraph()).toMatchSnapshot()
15611588
})
15621589

1563-
test('visible onChange',()=>{
1590+
test('visible onChange', () => {
15641591
const onChangeHandler = jest.fn()
15651592
const form = createForm({
1566-
initialValues:{
1567-
aa:123
1593+
initialValues: {
1594+
aa: 123
15681595
},
1569-
onChange:onChangeHandler
1596+
onChange: onChangeHandler
15701597
})
15711598
form.registerField({
1572-
name:'aa'
1599+
name: 'aa'
15731600
})
15741601
form.setFieldState('aa', state => {
15751602
state.visible = false

packages/core/src/index.ts

+8
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ export function createForm<FieldProps, VirtualFieldProps>(
9696
state.initialValue = initialValue
9797
if (!isValid(state.value)) {
9898
state.value = initialValue
99+
} else if (
100+
state.dataType === 'array' &&
101+
state.value &&
102+
state.value.length === 0
103+
) {
104+
state.value = initialValue
99105
}
100106
}
101107
}
@@ -441,6 +447,7 @@ export function createForm<FieldProps, VirtualFieldProps>(
441447
visible,
442448
display,
443449
computeState,
450+
dataType,
444451
useDirty,
445452
props
446453
}: Exclude<IFieldStateProps, 'dataPath' | 'nodePath'>): IField {
@@ -455,6 +462,7 @@ export function createForm<FieldProps, VirtualFieldProps>(
455462
nodePath,
456463
dataPath,
457464
computeState,
465+
dataType,
458466
useDirty: isValid(useDirty) ? useDirty : options.useDirty
459467
})
460468
field.subscription = {

packages/core/src/shared/model.ts

+46-5
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import {
77
Subscribable,
88
FormPath,
99
FormPathPattern,
10-
isValid
10+
isValid,
11+
toArr
1112
} from '@uform/shared'
1213
import produce, { Draft, setAutoFreeze } from 'immer'
1314
import {
@@ -21,6 +22,18 @@ const hasProxy = !!globalThisPolyfill.Proxy
2122

2223
setAutoFreeze(false)
2324

25+
const defaults = (...args:any[]):any=>{
26+
const result = {}
27+
each(args,(target)=>{
28+
each(target,(value,key)=>{
29+
if(isValid(value)){
30+
result[key] = value
31+
}
32+
})
33+
})
34+
return result
35+
}
36+
2437
export const createStateModel = <State = {}, Props = {}>(
2538
Factory: IStateModelFactory<State, Props>
2639
): IStateModelProvider<State, Props> => {
@@ -32,6 +45,7 @@ export const createStateModel = <State = {}, Props = {}>(
3245
useDirty?: boolean
3346
computeState?: (draft: State, prevState: State) => void
3447
}
48+
public cacheProps?: any
3549
public displayName?: string
3650
public dirtyNum: number
3751
public dirtys: StateDirtyMap<State>
@@ -44,10 +58,7 @@ export const createStateModel = <State = {}, Props = {}>(
4458
super()
4559
this.state = { ...Factory.defaultState }
4660
this.prevState = { ...Factory.defaultState }
47-
this.props = {
48-
...Factory.defaultProps,
49-
...defaultProps
50-
}
61+
this.props = defaults(Factory.defaultProps,defaultProps)
5162
this.dirtys = {}
5263
this.dirtyNum = 0
5364
this.stackCount = 0
@@ -100,6 +111,36 @@ export const createStateModel = <State = {}, Props = {}>(
100111
}
101112
}
102113

114+
watchProps = <T extends { [key: string]: any }>(
115+
props: T,
116+
keys: string[],
117+
callback: (
118+
changedProps: {
119+
[key: string]: any
120+
},
121+
props?: T
122+
) => void
123+
) => {
124+
if (!this.cacheProps) {
125+
this.cacheProps = { ...props }
126+
} else {
127+
let changeNum = 0
128+
let changedProps = {}
129+
toArr(keys).forEach((key: string) => {
130+
if (!isEqual(this.cacheProps[key], props[key])) {
131+
changeNum++
132+
changedProps[key] = props[key]
133+
}
134+
})
135+
if (changeNum > 0) {
136+
if (isFn(callback)) {
137+
callback(changedProps, props)
138+
}
139+
this.cacheProps = { ...props }
140+
}
141+
}
142+
}
143+
103144
setState = (
104145
callback: (state: State | Draft<State>) => State | void,
105146
silent = false

packages/core/src/state/field.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export const FieldState = createStateModel<IFieldState, IFieldStateProps>(
1010
static defaultState = {
1111
name: '',
1212
path: '',
13+
dataType: 'any',
1314
initialized: false,
1415
pristine: true,
1516
valid: true,
@@ -42,7 +43,8 @@ export const FieldState = createStateModel<IFieldState, IFieldStateProps>(
4243
}
4344

4445
static defaultProps = {
45-
path: ''
46+
path: '',
47+
dataType: 'any'
4648
}
4749

4850
private state: IFieldState
@@ -57,6 +59,7 @@ export const FieldState = createStateModel<IFieldState, IFieldStateProps>(
5759
this.dataPath = FormPath.getPath(props.dataPath)
5860
this.state.name = this.dataPath.entire
5961
this.state.path = this.nodePath.entire
62+
this.state.dataType = props.dataType || 'any'
6063
}
6164

6265
readValues({ value, values }: IFieldStateProps) {
@@ -68,9 +71,17 @@ export const FieldState = createStateModel<IFieldState, IFieldStateProps>(
6871
values = toArr(value)
6972
}
7073
}
74+
75+
values = toArr(values)
76+
77+
if(this.state.dataType === 'array'){
78+
value = toArr(value)
79+
values[0] = toArr(values[0])
80+
}
81+
7182
return {
7283
value,
73-
values: toArr(values)
84+
values
7485
}
7586
}
7687

packages/core/src/state/virtual-field.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,13 @@ export const VirtualFieldState = createStateModel<
4545
}
4646

4747
computeState(draft: IVirtualFieldState, prevState: IVirtualFieldState) {
48-
if (draft.mounted === true) {
48+
if (draft.mounted === true && draft.mounted !== prevState.mounted) {
4949
draft.unmounted = false
5050
}
5151
if (!isValid(draft.props)) {
5252
draft.props = prevState.props
5353
}
54-
if (draft.unmounted === true) {
54+
if (draft.unmounted === true && draft.unmounted !== prevState.unmounted) {
5555
draft.mounted = false
5656
}
5757
}

packages/core/src/types.ts

+13
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ export interface IStateModelProvider<S, P> {
120120

121121
export interface IFieldState<FieldProps = any> {
122122
displayName?: string
123+
dataType: string
123124
name: string
124125
path: string
125126
initialized: boolean
@@ -159,6 +160,7 @@ export interface IFieldStateProps<FieldProps = any> {
159160
path?: FormPathPattern
160161
nodePath?: FormPathPattern
161162
dataPath?: FormPathPattern
163+
dataType?: string
162164
name?: string
163165
value?: any
164166
values?: any[]
@@ -170,6 +172,7 @@ export interface IFieldStateProps<FieldProps = any> {
170172
visible?: boolean
171173
display?: boolean
172174
useDirty?: boolean
175+
useListMode?: boolean
173176
computeState?: (draft: IFieldState, prevState: IFieldState) => void
174177
}
175178

@@ -322,6 +325,16 @@ export interface IModel<S = {}, P = {}> extends Subscribable {
322325
setState: (callback?: (state: S | Draft<S>) => void, silent?: boolean) => void
323326
getSourceState: (callback?: (state: S) => any) => any
324327
setSourceState: (callback?: (state: S) => void) => void
328+
watchProps: <T extends { [key: string]: any }>(
329+
props: T,
330+
keys: string[],
331+
callback: (
332+
changedProps: {
333+
[key: string]: any
334+
},
335+
props: T
336+
) => void
337+
) => void
325338
hasChanged: (path?: FormPathPattern) => boolean
326339
isDirty: (key?: string) => boolean
327340
getDirtyInfo: () => StateDirtyMap<S>

packages/react-schema-renderer/src/__tests__/__snapshots__/markup.spec.tsx.snap

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Object {
2626
},
2727
"aa": Object {
2828
"active": false,
29+
"dataType": "string",
2930
"display": true,
3031
"displayName": "FieldState",
3132
"editable": true,
@@ -121,6 +122,7 @@ Object {
121122
},
122123
"NO_NAME_FIELD_$0.aa": Object {
123124
"active": false,
125+
"dataType": "string",
124126
"display": true,
125127
"displayName": "FieldState",
126128
"editable": true,

packages/react-schema-renderer/src/__tests__/__snapshots__/register.spec.tsx.snap

+3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Object {
2626
},
2727
"aa": Object {
2828
"active": false,
29+
"dataType": "string",
2930
"display": true,
3031
"displayName": "FieldState",
3132
"editable": true,
@@ -96,6 +97,7 @@ Object {
9697
},
9798
"aa": Object {
9899
"active": false,
100+
"dataType": "string",
99101
"display": true,
100102
"displayName": "FieldState",
101103
"editable": true,
@@ -181,6 +183,7 @@ Object {
181183
},
182184
"cc.aa": Object {
183185
"active": false,
186+
"dataType": "string",
184187
"display": true,
185188
"displayName": "FieldState",
186189
"editable": true,

packages/react-schema-renderer/src/components/SchemaField.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export const SchemaField: React.FunctionComponent<ISchemaFieldProps> = (
6060
path={path}
6161
initialValue={fieldSchema.default}
6262
props={fieldSchema.getSelfProps()}
63+
dataType={fieldSchema.type}
6364
triggerType={fieldSchema.getExtendsTriggerType()}
6465
editable={fieldSchema.getExtendsEditable()}
6566
visible={fieldSchema.getExtendsVisible()}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React from 'react'
2+
import { IFieldStateUIProps } from '../types'
3+
import { Field } from './Field'
4+
5+
export const FieldList: React.FC<IFieldStateUIProps> = props => {
6+
return React.createElement(Field, {
7+
...props,
8+
dataType: 'array'
9+
})
10+
}
11+
12+
FieldList.displayName = 'ReactInternalFieldList'
13+
14+
FieldList.defaultProps = {
15+
path: '',
16+
triggerType: 'onChange'
17+
}

packages/react/src/hooks/useDirty.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react'
22
import { isEqual } from '@uform/shared'
33

44
export const useDirty = (input: any = {}, keys: string[] = []) => {
5-
const ref = React.useRef<any>({ data: {...input}, dirtys: {}, num: 0 })
5+
const ref = React.useRef<any>({ data: { ...input }, dirtys: {}, num: 0 })
66
ref.current.num = 0
77
keys.forEach(key => {
88
if (!isEqual(input[key], ref.current.data[key])) {

0 commit comments

Comments
 (0)