Skip to content

Commit

Permalink
fix(@uform/core/react): fix #613 #615 (#618)
Browse files Browse the repository at this point in the history
* fix(@uform/shared): fix isValid

* fix(@uform/core): fix visible changed but not trigger onChange callback

* fix(@uform/core): fix #613 and #615

* refactor(@uform/core): improve code
  • Loading branch information
janryWang authored Jan 19, 2020
1 parent 6ca8809 commit 8dc609f
Show file tree
Hide file tree
Showing 16 changed files with 360 additions and 80 deletions.
109 changes: 109 additions & 0 deletions packages/core/src/__tests__/__snapshots__/index.spec.ts.snap

Large diffs are not rendered by default.

52 changes: 48 additions & 4 deletions packages/core/src/__tests__/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,33 @@ describe('createForm', () => {
expect(form.getFormGraph()).toMatchSnapshot()
})

const sleep = (d=1000)=>new Promise((resolve)=>{
setTimeout(()=>{
resolve()
},d)
})

test('invalid initialValue will not trigger validate', async () => {
const form = createForm()
const field = form.registerField({
name: 'aa',
rules:[{
required:true
}]
})
const mutators = form.createMutators(field)
field.subscribe(() => {
mutators.validate({ throwErrors: false })
})
form.setFormState(state => {
state.initialValues = {
aa: null
}
})
await sleep(10)
expect(field.getState(state=>state.errors).length).toEqual(1)
})

test('lifecycles', () => {
const onFormInit = jest.fn()
const onFieldInit = jest.fn()
Expand Down Expand Up @@ -437,9 +464,9 @@ describe('clearErrors', () => {
expect(form.getFormState(state => state.errors)).toEqual([])
})

test('wildcard path', async () => { })
test('wildcard path', async () => {})

test('effect', async () => { })
test('effect', async () => {})
})

describe('validate', () => {
Expand Down Expand Up @@ -487,7 +514,7 @@ describe('validate', () => {

try {
await form.submit()
} catch (e) { }
} catch (e) {}
expect(onValidateFailedTrigger).toBeCalledTimes(1)
})

Expand Down Expand Up @@ -515,7 +542,7 @@ describe('validate', () => {
}) // CustomValidator error
try {
await form.submit()
} catch (e) { }
} catch (e) {}
expect(onValidateFailedTrigger).toBeCalledTimes(1)
})

Expand Down Expand Up @@ -1560,6 +1587,23 @@ describe('major sences', () => {
expect(form.getFormGraph()).toMatchSnapshot()
})

test('visible onChange', () => {
const onChangeHandler = jest.fn()
const form = createForm({
initialValues: {
aa: 123
},
onChange: onChangeHandler
})
form.registerField({
name: 'aa'
})
form.setFieldState('aa', state => {
state.visible = false
})
expect(onChangeHandler).toBeCalledTimes(1)
})

test('deep nested visible(root)', () => {
const form = createForm()
form.registerField({ path: 'aa', value: {} })
Expand Down
97 changes: 66 additions & 31 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ export function createForm<FieldProps, VirtualFieldProps>(
state.initialValue = initialValue
if (!isValid(state.value)) {
state.value = initialValue
} else if (
/array/gi.test(state.dataType) &&
state.value &&
state.value.length === 0
) {
state.value = initialValue
}
}
}
Expand All @@ -112,7 +118,7 @@ export function createForm<FieldProps, VirtualFieldProps>(
const updateFields = (field: IField | IVirtualField) => {
if (isField(field)) {
field.setState(state => {
if (state.visible) {
if (state.visible || state.unmounted) {
if (valuesChanged) {
syncFieldValues(state)
}
Expand All @@ -122,14 +128,14 @@ export function createForm<FieldProps, VirtualFieldProps>(
} else {
//缓存变化,等字段重新显示的时候再执行
if (valuesChanged) {
env.visiblePendingFields[state.name] =
env.visiblePendingFields[state.name] || {}
env.visiblePendingFields[state.name].values = true
env.hiddenPendingFields[state.name] =
env.hiddenPendingFields[state.name] || {}
env.hiddenPendingFields[state.name].values = true
}
if (initialValuesChanged) {
env.visiblePendingFields[state.name] =
env.visiblePendingFields[state.name] || {}
env.visiblePendingFields[state.name].initialValues = true
env.hiddenPendingFields[state.name] =
env.hiddenPendingFields[state.name] || {}
env.hiddenPendingFields[state.name].initialValues = true
}
}
})
Expand Down Expand Up @@ -227,6 +233,28 @@ export function createForm<FieldProps, VirtualFieldProps>(
const errorsChanged = field.isDirty('errors')
const userUpdateFieldPath =
env.userUpdateFields[env.userUpdateFields.length - 1]

const syncField = () => {
if (env.hiddenPendingFields[published.name]) {
field.setState((state: IFieldState) => {
if (env.hiddenPendingFields[state.name].values) {
syncFieldValues(state)
}
if (env.hiddenPendingFields[state.name].initialValues) {
syncFieldIntialValues(state)
}
delete env.hiddenPendingFields[state.name]
})
}
}

const notifyFormValuesChange = () => {
if (isFn(options.onChange)) {
options.onChange(state.getSourceState(state => clone(state.values)))
}
heart.publish(LifeCycleTypes.ON_FORM_VALUES_CHANGE, state)
}

if (initializedChanged) {
heart.publish(LifeCycleTypes.ON_FIELD_INIT, field)
const isEmptyValue = !isValid(published.value)
Expand All @@ -239,34 +267,34 @@ export function createForm<FieldProps, VirtualFieldProps>(
})
}
}
const wasHidden =
published.visible == false || published.unmounted === true
if (valueChanged) {
userUpdating(field, () => {
setFormValuesIn(path, published.value)
})
if (!wasHidden) {
userUpdating(field, () => {
setFormValuesIn(path, published.value)
})
}
heart.publish(LifeCycleTypes.ON_FIELD_VALUE_CHANGE, field)
}
if (initialValueChanged) {
setFormInitialValuesIn(path, published.initialValue)
if (!wasHidden) {
setFormInitialValuesIn(path, published.initialValue)
}
heart.publish(LifeCycleTypes.ON_FIELD_INITIAL_VALUE_CHANGE, field)
}
if (displayChanged || visibleChanged) {
if (visibleChanged) {
if (!published.visible) {
deleteFormValuesIn(path, true)
} else {
setFormValuesIn(path, published.value)
if (env.visiblePendingFields[published.name]) {
field.setState((state: IFieldState) => {
if (env.visiblePendingFields[state.name].values) {
syncFieldValues(state)
}
if (env.visiblePendingFields[state.name].initialValues) {
syncFieldIntialValues(state)
}
delete env.visiblePendingFields[state.name]
})
userUpdating(field, () => {
if (!published.visible) {
deleteFormValuesIn(path, true)
//考虑到隐藏删值,不应该同步子树,但是需要触发表单变化事件
notifyFormValuesChange()
} else {
setFormValuesIn(path, published.value)
syncField()
}
}
})
}
graph.eachChildren(path, childState => {
childState.setState((state: IFieldState<FieldProps>) => {
Expand All @@ -286,8 +314,11 @@ export function createForm<FieldProps, VirtualFieldProps>(
userUpdating(field, () => {
if (published.unmounted) {
deleteFormValuesIn(path, true)
//考虑到隐藏删值,不应该同步子树,但是需要触发表单变化事件
notifyFormValuesChange()
} else {
setFormValuesIn(path, published.value)
syncField()
}
})
heart.publish(LifeCycleTypes.ON_FIELD_UNMOUNT, field)
Expand Down Expand Up @@ -416,6 +447,7 @@ export function createForm<FieldProps, VirtualFieldProps>(
visible,
display,
computeState,
dataType,
useDirty,
props
}: Exclude<IFieldStateProps, 'dataPath' | 'nodePath'>): IField {
Expand All @@ -430,6 +462,7 @@ export function createForm<FieldProps, VirtualFieldProps>(
nodePath,
dataPath,
computeState,
dataType,
useDirty: isValid(useDirty) ? useDirty : options.useDirty
})
field.subscription = {
Expand Down Expand Up @@ -780,7 +813,10 @@ export function createForm<FieldProps, VirtualFieldProps>(
return arr
},
validate(opts?: IFormExtendedValidateFieldOptions) {
return validate(field.getSourceState(state => state.path), opts)
return validate(
field.getSourceState(state => state.path),
opts
)
}
}
}
Expand Down Expand Up @@ -1041,9 +1077,8 @@ export function createForm<FieldProps, VirtualFieldProps>(

function userUpdating(field: IField | IVirtualField, fn?: () => void) {
if (!field) return
const nodePath = field.getSourceState(state => state.path)
if (nodePath)
env.userUpdateFields.push(field.getSourceState(state => state.path))
const nodePath = field.state.path
if (nodePath) env.userUpdateFields.push(nodePath)
if (isFn(fn)) {
fn()
}
Expand Down Expand Up @@ -1223,7 +1258,7 @@ export function createForm<FieldProps, VirtualFieldProps>(
userUpdateFields: [],
taskIndexes: {},
removeNodes: {},
visiblePendingFields: {},
hiddenPendingFields: {},
lastShownStates: {},
submittingTask: undefined
}
Expand Down
51 changes: 46 additions & 5 deletions packages/core/src/shared/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
Subscribable,
FormPath,
FormPathPattern,
isValid
isValid,
toArr
} from '@uform/shared'
import produce, { Draft, setAutoFreeze } from 'immer'
import {
Expand All @@ -21,6 +22,18 @@ const hasProxy = !!globalThisPolyfill.Proxy

setAutoFreeze(false)

const defaults = (...args:any[]):any=>{
const result = {}
each(args,(target)=>{
each(target,(value,key)=>{
if(isValid(value)){
result[key] = value
}
})
})
return result
}

export const createStateModel = <State = {}, Props = {}>(
Factory: IStateModelFactory<State, Props>
): IStateModelProvider<State, Props> => {
Expand All @@ -32,6 +45,7 @@ export const createStateModel = <State = {}, Props = {}>(
useDirty?: boolean
computeState?: (draft: State, prevState: State) => void
}
public cacheProps?: any
public displayName?: string
public dirtyNum: number
public dirtys: StateDirtyMap<State>
Expand All @@ -44,10 +58,7 @@ export const createStateModel = <State = {}, Props = {}>(
super()
this.state = { ...Factory.defaultState }
this.prevState = { ...Factory.defaultState }
this.props = {
...Factory.defaultProps,
...defaultProps
}
this.props = defaults(Factory.defaultProps,defaultProps)
this.dirtys = {}
this.dirtyNum = 0
this.stackCount = 0
Expand Down Expand Up @@ -100,6 +111,36 @@ export const createStateModel = <State = {}, Props = {}>(
}
}

watchProps = <T extends { [key: string]: any }>(
props: T,
keys: string[],
callback: (
changedProps: {
[key: string]: any
},
props?: T
) => void
) => {
if (!this.cacheProps) {
this.cacheProps = { ...props }
} else {
let changeNum = 0
let changedProps = {}
toArr(keys).forEach((key: string) => {
if (!isEqual(this.cacheProps[key], props[key])) {
changeNum++
changedProps[key] = props[key]
}
})
if (changeNum > 0) {
if (isFn(callback)) {
callback(changedProps, props)
}
this.cacheProps = { ...props }
}
}
}

setState = (
callback: (state: State | Draft<State>) => State | void,
silent = false
Expand Down
Loading

0 comments on commit 8dc609f

Please sign in to comment.