Skip to content

Commit

Permalink
test(project): update mobx => @formily/reactive
Browse files Browse the repository at this point in the history
  • Loading branch information
janryWang committed Mar 11, 2021
1 parent c0fb81e commit 7ae0a92
Show file tree
Hide file tree
Showing 11 changed files with 82 additions and 76 deletions.
8 changes: 4 additions & 4 deletions packages/core/src/models/Field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
parseValidatorDescriptions,
} from '@formily/validator'
import {
defineModel,
define,
observable,
reaction,
batch,
Expand Down Expand Up @@ -145,7 +145,7 @@ export class Field<
}

protected makeObservable() {
defineModel(this, {
define(this, {
title: observable.ref,
description: observable.ref,
dataSource: observable.ref,
Expand All @@ -163,8 +163,8 @@ export class Field<
inputValues: observable.ref,
decoratorType: observable.ref,
componentType: observable.ref,
decoratorProps: observable.shallow,
componentProps: observable.shallow,
decoratorProps: observable,
componentProps: observable,
validator: observable,
feedbacks: observable,
component: observable.computed,
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/models/Form.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
defineModel,
define,
observable,
batch,
toJS,
Expand Down Expand Up @@ -120,7 +120,7 @@ export class Form<ValueType extends object = any> {
}

protected makeObservable() {
defineModel(this, {
define(this, {
fields: observable.shallow,
initialized: observable.ref,
validating: observable.ref,
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/models/Graph.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineModel, batch } from '@formily/reactive'
import { define, batch } from '@formily/reactive'
import { each, FormPath } from '@formily/shared'
import { IFormGraph } from '../types'
import { Form } from './Form'
Expand All @@ -14,7 +14,7 @@ export class Graph {

constructor(form: Form) {
this.form = form
defineModel(this, {
define(this, {
setGraph: batch,
})
}
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/models/VoidField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
isValid,
toArr,
} from '@formily/shared'
import { defineModel, observable, autorun, batch } from '@formily/reactive'
import { define, observable, autorun, batch } from '@formily/reactive'
import {
JSXComponent,
JSXComponenntProps,
Expand Down Expand Up @@ -93,7 +93,7 @@ export class VoidField<Decorator = any, Component = any, TextType = any> {
}

protected makeObservable() {
defineModel(this, {
define(this, {
title: observable.ref,
description: observable.ref,
selfDisplay: observable.ref,
Expand All @@ -102,9 +102,9 @@ export class VoidField<Decorator = any, Component = any, TextType = any> {
mounted: observable.ref,
unmounted: observable.ref,
decoratorType: observable.ref,
decoratorProps: observable.shallow,
componentType: observable.ref,
componentProps: observable.shallow,
decoratorProps: observable,
componentProps: observable,
display: observable.computed,
pattern: observable.computed,
hidden: observable.computed,
Expand Down
6 changes: 4 additions & 2 deletions packages/react/src/__tests__/field.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ test('useAttch', () => {
expect(form.query('bb').take().mounted).toBeTruthy()
})

test('useFormEffects', () => {
test('useFormEffects', async () => {
const form = createForm()
const CustomField = observer((props: { tag?: string }) => {
const field = useField<Formily.Core.Models.Field>()
Expand All @@ -164,7 +164,9 @@ test('useFormEffects', () => {
aa.setValue('123')
}
})
expect(queryByTestId('custom-value').textContent).toEqual('123')
await waitFor(() => {
expect(queryByTestId('custom-value').textContent).toEqual('123')
})
rerender(
<FormProvider form={form}>
<Field name="aa" decorator={[Decorator]} component={[Input]} />
Expand Down
44 changes: 42 additions & 2 deletions packages/reactive-react/src/hooks/useForceUpdate.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,53 @@
import { useCallback, useState } from 'react'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import ReactDOM from 'react-dom'

const batchUpdate =
React['batchUpdate'] ||
ReactDOM['batchUpdate'] ||
ReactDOM['unstable_batchedUpdates'] ||
((callback: any) => callback())

const EMPTY_ARRAY: any[] = []
const RENDER_COUNT = { value: 0 }
const RENDER_QUEUE = new Set<() => void>()

export function useForceUpdate() {
const [, setTick] = useState(0)
const unmountRef = useRef(false)

const update = useCallback(() => {
setTick((tick) => tick + 1)
if (RENDER_COUNT.value === 0) {
if (unmountRef.current) return
setTick((tick) => {
return tick + 1
})
} else {
if (!RENDER_QUEUE.has(update)) {
RENDER_QUEUE.add(update)
}
}
}, EMPTY_ARRAY)

RENDER_COUNT.value++

useEffect(() => {
RENDER_COUNT.value--
if (RENDER_COUNT.value === 0) {
batchUpdate(() => {
RENDER_QUEUE.forEach((update) => {
RENDER_QUEUE.delete(update)
update()
})
})
}
})

useEffect(() => {
unmountRef.current = false
return () => {
unmountRef.current = true
}
}, [])

return update
}
22 changes: 7 additions & 15 deletions packages/reactive-react/src/hooks/useObserver.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,30 @@
import React from 'react'
import ReactDOM from 'react-dom'
import { Tracker } from '@formily/reactive'
import { isFn } from '@formily/shared'
import { GarbageCollector } from '../gc'
import { IObserverOptions } from '../types'
import { useForceUpdate } from './useForceUpdate'

const batchUpdate =
React['batchUpdate'] ||
ReactDOM['batchUpdate'] ||
ReactDOM['unstable_batchedUpdates']

class AutoCollector {}

export const useObserver = <T extends () => any>(
view: T,
options?: IObserverOptions
): ReturnType<T> => {
const forceUpdate = useForceUpdate()
const unmountRef = React.useRef(false)
const gcRef = React.useRef<GarbageCollector>()

const tracker = React.useMemo(() => {
const updater = () => {
if (isFn(batchUpdate)) {
batchUpdate(() => forceUpdate())
} else {
forceUpdate()
}
}
return new Tracker(() => {
if (isFn(options?.scheduler)) {
options.scheduler(updater)
options.scheduler(forceUpdate)
} else {
updater()
forceUpdate()
}
})
}, [])

//StrictMode/ConcurrentMode会导致组件无法正确触发Unmount,所以只能自己做垃圾回收
if (!gcRef.current) {
const target = new AutoCollector()
Expand All @@ -47,8 +37,10 @@ export const useObserver = <T extends () => any>(
}

React.useEffect(() => {
unmountRef.current = false
gcRef.current.close()
return () => {
unmountRef.current = true
if (tracker) {
tracker.dispose()
}
Expand Down
33 changes: 0 additions & 33 deletions packages/reactive/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,3 @@
> Web Reactive Library Like Mobx
## QuikStart

```tsx
import React from 'react'
import { createForm, onFieldChange } from '@formily/core'

const attach = <T extends { onMount: () => void }>(target: T): T => {
target.onMount()
return target
}

const form = attach(createForm({}))
const field = attach(
form.createField({
name: 'aa',
required: true,
})
)
field.onInput('').then(() => {
console.log(field.value, field.errors)
})

export default () => (
<button
onClick={() => {
field.onInput('123').then(() => {
console.log(field.value, field.errors)
})
}}
>
click
</button>
)
```
14 changes: 7 additions & 7 deletions packages/reactive/src/__tests__/define.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineModel, observable, autorun } from '..'
import { define, observable, autorun } from '..'
import { observe } from '../observe'
import { FormPath } from '@formily/shared'
import { batch } from '../batch'
Expand All @@ -8,7 +8,7 @@ describe('makeObservable', () => {
const target: any = {
aa: {},
}
defineModel(target, {
define(target, {
aa: observable,
})
const handler = jest.fn()
Expand All @@ -29,7 +29,7 @@ describe('makeObservable', () => {
const target: any = {
aa: {},
}
defineModel(target, {
define(target, {
aa: observable.shallow,
})
const handler = jest.fn()
Expand All @@ -50,7 +50,7 @@ describe('makeObservable', () => {
})
test('box annotation', () => {
const target: any = {}
defineModel(target, {
define(target, {
aa: observable.box,
})
const handler = jest.fn()
Expand All @@ -68,7 +68,7 @@ describe('makeObservable', () => {
})
test('ref annotation', () => {
const target: any = {}
defineModel(target, {
define(target, {
aa: observable.ref,
})
const handler = jest.fn()
Expand All @@ -92,7 +92,7 @@ describe('makeObservable', () => {
target.aa.cc = 312
},
}
defineModel(target, {
define(target, {
aa: observable,
setData: batch,
})
Expand All @@ -113,7 +113,7 @@ describe('makeObservable', () => {
return this.aa + this.bb
},
}
defineModel(target, {
define(target, {
aa: observable,
bb: observable,
cc: observable.computed,
Expand Down
5 changes: 3 additions & 2 deletions packages/reactive/src/annotations/computed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ export interface IComputed {

export const computed: IComputed = createAnnotation(
({ target, key, value }) => {
const initialValue = Symbol('initialValue')
const store = {
value: undefined,
value: initialValue,
}

const proxy = {
Expand Down Expand Up @@ -61,7 +62,7 @@ export const computed: IComputed = createAnnotation(
function compute() {
const oldValue = store.value
store.value = getter?.call?.(context)
if (oldValue === store.value) return
if (oldValue === store.value || oldValue === initialValue) return
batchStart()
runReactionsFromTargetKey({
target: context,
Expand Down
10 changes: 7 additions & 3 deletions packages/reactive/src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,24 @@ import { each, isFn, reduce } from '@formily/shared'
import { buildTreeNode } from './traverse'
import { observable } from './observable'
import { createObservable, getObservableMaker } from './internals'
import { isObservable, isAnnotation } from './shared'
import { isObservable, isAnnotation, isSupportObservable } from './shared'
import { Annotations } from './types'
import { batch } from './batch'
import { ProxyRaw, RawProxy } from './environment'

export function defineModel<Target extends object = any>(
export function define<Target extends object = any>(
target: Target,
annotations?: Annotations<Target>,
traverse = createObservable
) {
if (isObservable(target)) return target
if (!isSupportObservable(target)) return target
buildTreeNode({
value: target,
traverse,
})
ProxyRaw.set(target, target)
RawProxy.set(target, target)
return observable(target, ({ target, value }) => {
if (target) return target
each(annotations, (annotation, key) => {
Expand All @@ -42,5 +46,5 @@ export function model<Target extends object = any>(target: Target) {
}
return buf
}, {})
return defineModel(target, annotations)
return define(target, annotations)
}

0 comments on commit 7ae0a92

Please sign in to comment.