Skip to content

Commit

Permalink
fix(react): fix x-component-props is not reactive
Browse files Browse the repository at this point in the history
  • Loading branch information
janryWang committed Sep 14, 2021
1 parent 138c2e4 commit d91ebff
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 62 deletions.
15 changes: 8 additions & 7 deletions packages/core/src/shared/internals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
toArr,
isNumberLike,
shallowClone,
clone,
isEqual,
} from '@formily/shared'
import {
Expand Down Expand Up @@ -44,7 +45,7 @@ import {
RESPONSE_REQUEST_DURATION,
ReservedProperties,
GlobalState,
NumberIndexReg
NumberIndexReg,
} from './constants'

export const isHTMLInputEvent = (event: any, stopPropagation = true) => {
Expand Down Expand Up @@ -711,11 +712,11 @@ export const applyValuesPatch = (
path: Array<string | number>,
source: any
) => {
const merge = (path: Array<string | number>, source: any) => {
const update = (path: Array<string | number>, source: any) => {
if (path.length) {
form.setValuesIn(path, toJS(source))
form.setValuesIn(path, clone(source))
} else {
Object.assign(form.values, toJS(source))
Object.assign(form.values, clone(source))
}
}

Expand All @@ -724,7 +725,7 @@ export const applyValuesPatch = (
const targetField = form.query(path).take()
if (isEmpty(targetValue)) {
if (isEmpty(source)) return
merge(path, source)
update(path, source)
} else {
const arrA = isArr(targetValue)
const arrB = isArr(source)
Expand All @@ -738,10 +739,10 @@ export const applyValuesPatch = (
} else {
if (targetField) {
if (!isVoidField(targetField) && !targetField.modified) {
merge(path, source)
update(path, source)
}
} else {
merge(path, source)
update(path, source)
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions packages/json-schema/src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,12 @@ export const patchCompile = (
sourceState: any,
scope: any
) => {
traverse(sourceState, (value, path) => {
traverse(sourceState, (value, pattern) => {
const path = FormPath.parse(pattern)
const compiled = compile(value, scope)
const key = path.segments[0]
if (compiled === undefined) return
if (hasOwnProperty.call(targetState, path[0])) {
if (hasOwnProperty.call(targetState, key)) {
untracked(() => FormPath.setIn(targetState, path, compiled))
}
})
Expand Down
15 changes: 9 additions & 6 deletions packages/json-schema/src/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,22 +102,25 @@ export const createDataSource = (source: any[]) => {

export const patchStateFormSchema = (
targetState: any,
path: any[],
pattern: any[],
compiled: any
) => {
untracked(() => {
const isEnum = path[0] === 'enum' && isArr(compiled)
const schemaMapKey = SchemaStateMap[path[0]]
const path = FormPath.parse(pattern)
const segments = path.segments
const key = segments[0]
const isEnum = key === 'enum' && isArr(compiled)
const schemaMapKey = SchemaStateMap[key]
if (schemaMapKey) {
FormPath.setIn(
targetState,
[schemaMapKey].concat(path.slice(1)),
[schemaMapKey].concat(segments.slice(1)),
isEnum ? createDataSource(compiled) : compiled
)
} else {
const isValidatorKey = SchemaValidatorKeys[path[0]]
const isValidatorKey = SchemaValidatorKeys[key]
if (isValidatorKey) {
targetState['setValidatorRule'](path[0], compiled)
targetState['setValidatorRule'](key, compiled)
}
}
})
Expand Down
76 changes: 76 additions & 0 deletions packages/react/src/__tests__/schema.markup.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -650,3 +650,79 @@ test('expression x-value', async () => {
expect(queryByText('100')).not.toBeNull()
})
})

test('nested update component props with expression', async () => {
const form = createForm({
values: {
aaa: 'xxx',
},
})
const SchemaField = createSchemaField({
components: {
Text: (props) => <div>{props.aa?.bb?.cc}</div>,
},
})

const { queryByText } = render(
<FormProvider form={form}>
<SchemaField>
<SchemaField.String name="aaa" x-component="Text" />
<SchemaField.String
name="bbb"
x-component="Text"
x-component-props={{ aa: { bb: { cc: '{{$form.values.aaa}}' } } }}
/>
</SchemaField>
</FormProvider>
)
await waitFor(() => {
expect(queryByText('xxx')).not.toBeNull()
})
act(() => {
form.values.aaa = '10'
})
await waitFor(() => {
expect(queryByText('10')).not.toBeNull()
})
})

test('nested update component props with x-reactions', async () => {
const form = createForm({
values: {
aaa: 'xxx',
},
})
const SchemaField = createSchemaField({
components: {
Text: (props) => <div>{props.aa?.bb?.cc}</div>,
},
})

const { queryByText } = render(
<FormProvider form={form}>
<SchemaField>
<SchemaField.String name="aaa" x-component="Text" />
<SchemaField.String
name="bbb"
x-component="Text"
x-reactions={{
fulfill: {
schema: {
'x-component-props.aa.bb.cc': '{{$form.values.aaa}}',
} as any,
},
}}
/>
</SchemaField>
</FormProvider>
)
await waitFor(() => {
expect(queryByText('xxx')).not.toBeNull()
})
act(() => {
form.values.aaa = '10'
})
await waitFor(() => {
expect(queryByText('10')).not.toBeNull()
})
})
14 changes: 3 additions & 11 deletions packages/react/src/components/ReactiveField.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { Fragment, useContext } from 'react'
import { toJS } from '@formily/reactive'
import { observer } from '@formily/reactive-react'
import { isFn, FormPath } from '@formily/shared'
import { isVoidField, GeneralField, Form } from '@formily/core'
Expand Down Expand Up @@ -45,12 +46,7 @@ const ReactiveInternal: React.FC<IReactiveFieldProps> = (props) => {

return React.createElement(
finalComponent,
{
...field.decorator[1],
style: {
...field.decorator[1]?.style,
},
},
toJS(field.decorator[1]),
children
)
}
Expand Down Expand Up @@ -85,16 +81,12 @@ const ReactiveInternal: React.FC<IReactiveFieldProps> = (props) => {
const finalComponent =
FormPath.getIn(options?.components, field.component[0]) ??
field.component[0]

return React.createElement(
finalComponent,
{
disabled,
readOnly,
...field.component[1],
style: {
...field.component[1]?.style,
},
...toJS(field.component[1]),
value,
onChange,
onFocus,
Expand Down
46 changes: 13 additions & 33 deletions packages/reactive/src/externals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,40 +84,21 @@ export const raw = <T>(target: T): T => ProxyRaw.get(target as any)
export const toJS = <T>(values: T): T => {
const visited = new WeakSet<any>()
const _toJS: typeof toJS = (values: any) => {
if (visited.has(values)) {
return values
}
if (isArr(values)) {
if (visited.has(values)) {
return values
}
const originValues = values
if (ProxyRaw.has(values as any)) {
values = ProxyRaw.get(values as any)
if (isObservable(values)) {
visited.add(values)
const res: any = []
values.forEach((item: any) => {
res.push(_toJS(item))
})
return res
}
visited.add(originValues)
const res: any = []
values.forEach((item: any) => {
res.push(_toJS(item))
})
return res
} else if (isPlainObj(values)) {
if (visited.has(values)) {
return values
}
const originValues = values
if (ProxyRaw.has(values as any)) {
values = ProxyRaw.get(values as any)
}
if ('$$typeof' in values && '_owner' in values) {
return values
} else if (values['_isAMomentObject']) {
return values
} else if (values['_isJSONSchemaObject']) {
return values
} else if (isFn(values['toJS'])) {
return values['toJS']()
} else if (isFn(values['toJSON'])) {
return values['toJSON']()
} else {
visited.add(originValues)
if (isObservable(values)) {
visited.add(values)
const res: any = {}
for (const key in values) {
if (hasOwnProperty.call(values, key)) {
Expand All @@ -126,9 +107,8 @@ export const toJS = <T>(values: T): T => {
}
return res
}
} else {
return values
}
return values
}

return _toJS(values)
Expand Down
6 changes: 3 additions & 3 deletions packages/vue/src/components/ReactiveField.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { defineComponent, inject, ref } from 'vue-demi'
import { isVoidField } from '@formily/core'
import { clone, FormPath } from '@formily/shared'
import { FormPath } from '@formily/shared'
import { observer } from '@formily/reactive-vue'
import { toJS } from '@formily/reactive'
import { SchemaOptionsSymbol } from '../shared'
Expand Down Expand Up @@ -95,7 +95,7 @@ export default observer(
optionsRef.value?.components,
field.decorator[0]
) ?? field.decorator[0]) as VueComponent
const decoratorData = clone(field.decorator[1]) || {}
const decoratorData = toJS(field.decorator[1]) || {}
const style = decoratorData?.style
delete decoratorData.style
return {
Expand Down Expand Up @@ -133,7 +133,7 @@ export default observer(
optionsRef.value?.components,
field.component[0]
) ?? field.component[0]) as VueComponent
const originData = clone(field.component[1]) || {}
const originData = toJS(field.component[1]) || {}
const events = {} as Record<string, any>
const originChange = originData['@change'] || originData['onChange']
const originFocus = originData['@focus'] || originData['onFocus']
Expand Down

0 comments on commit d91ebff

Please sign in to comment.