Skip to content

Commit 670fadb

Browse files
JohnIsOnTheRoadjanryWang
authored andcommitted
feat(@uform/react): remove raf and fix unittest (alibaba#422)
1 parent 908882a commit 670fadb

File tree

4 files changed

+203
-23
lines changed

4 files changed

+203
-23
lines changed

packages/react/README.md

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ npm install --save @uform/react
3737
- [`Async Linkage`](#Async-Linkage)
3838
- [`Linkage Validation`](#Linkage-Validation)
3939
- [`Complex Linkage`](#Complex-Linkage)
40+
- [`Reuse Effects`](#Reuse-Effects)
4041
- [`Combo`](#Combo)
4142
- [`Provide and FormSpy`](#Provide-and-FormSpy)
4243
- [`Deconstruction`](#Deconstruction)
@@ -1022,6 +1023,94 @@ const App = () => {
10221023
ReactDOM.render(<App />, document.getElementById('root'))
10231024
```
10241025

1026+
#### Reuse Effects
1027+
1028+
Make your own reusable effects.
1029+
1030+
```jsx
1031+
import React from 'react'
1032+
import ReactDOM from 'react-dom'
1033+
import { Form, Field, createFormActions, FormEffectHooks } from '@uform/react'
1034+
1035+
1036+
const InputField = props => (
1037+
<Field {...props}>
1038+
{({ state, mutators }) => {
1039+
const loading = state.props.loading
1040+
return <React.Fragment>
1041+
{ props.label && <label>{props.label}</label> }
1042+
{ loading ? ' loading... ' : <input
1043+
disabled={!state.editable}
1044+
value={state.value || ''}
1045+
onChange={mutators.change}
1046+
onBlur={mutators.blur}
1047+
onFocus={mutators.focus}
1048+
/> }
1049+
<span style={{ color: 'red' }}>{state.errors}</span>
1050+
<span style={{ color: 'orange' }}>{state.warnings}</span>
1051+
</React.Fragment>
1052+
}}
1053+
</Field>
1054+
)
1055+
const CheckedField = props => (
1056+
<Field {...props}>
1057+
{({ state, mutators }) => {
1058+
const loading = state.props.loading
1059+
return <React.Fragment>
1060+
{ props.label && <label>{props.label}</label> }
1061+
{ loading ? ' loading... ' : <input type="checkbox" onChange={() => {
1062+
mutators.change(!state.value)
1063+
}} checked={!!state.value} /> }
1064+
<span style={{ color: 'red' }}>{state.errors}</span>
1065+
<span style={{ color: 'orange' }}>{state.warnings}</span>
1066+
</React.Fragment>
1067+
}}
1068+
</Field>
1069+
)
1070+
1071+
const { onFormMount$, onFieldValueChange$ } = FormEffectHooks
1072+
const getEffects = ()=>{
1073+
const actions = createFormActions()
1074+
onFormMount$().subscribe(() => {
1075+
actions.setFieldState('a~', state => state.visible = false)
1076+
})
1077+
1078+
onFieldValueChange$('trigger').subscribe((triggerState) => {
1079+
actions.setFieldState('a~', state => {
1080+
state.visible = triggerState.value
1081+
})
1082+
})
1083+
1084+
onFieldValueChange$('a').subscribe((fieldState) => {
1085+
actions.setFieldState('a-copy', state => {
1086+
state.value = fieldState.value
1087+
})
1088+
})
1089+
}
1090+
1091+
const actions = createFormActions()
1092+
const App = () => {
1093+
return (
1094+
<Form
1095+
actions={actions}
1096+
effects={() => {
1097+
getEffects()
1098+
}}
1099+
>
1100+
<CheckedField name="trigger" label="show/hide" />
1101+
<div>
1102+
<InputField label="a" name="a" />
1103+
</div>
1104+
<div>
1105+
<InputField label="a-copy" name="a-copy" />
1106+
</div>
1107+
</Form>
1108+
)
1109+
}
1110+
1111+
ReactDOM.render(<App />, document.getElementById('root'))
1112+
```
1113+
10251114
#### Combo
10261115

10271116
Example:Combo value of username and age. Check [FormSpy](#FormSpy) for more inforation.
@@ -1610,7 +1699,7 @@ const App = () => {
16101699
<Field name="user" initialValue={{}}>
16111700
{({ state, mutator }) => {
16121701
return (
1613-
<VirtualField name="layout">
1702+
<VirtualField name="user.layout">
16141703
{({ state: layoutState }) => {
16151704
return (
16161705
<Layout

packages/react/README.zh-cn.md

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ npm install --save @uform/react
3535
- [`异步联动`](#异步联动)
3636
- [`联动校验`](#联动校验)
3737
- [`复杂联动`](#复杂联动)
38+
- [`复用Effects`](#复用Effects)
3839
- [`combo字段`](#combo字段)
3940
- [`跨文件消费表单数据`](#跨文件消费表单数据)
4041
- [`简单解构`](#简单解构)
@@ -1027,6 +1028,96 @@ const App = () => {
10271028
ReactDOM.render(<App />, document.getElementById('root'))
10281029
```
10291030

1031+
#### 复用Effects
1032+
1033+
自定义可复用的effects
1034+
1035+
1036+
```jsx
1037+
import React from 'react'
1038+
import ReactDOM from 'react-dom'
1039+
import { Form, Field, createFormActions, FormEffectHooks } from '@uform/react'
1040+
1041+
1042+
const InputField = props => (
1043+
<Field {...props}>
1044+
{({ state, mutators }) => {
1045+
const loading = state.props.loading
1046+
return <React.Fragment>
1047+
{ props.label && <label>{props.label}</label> }
1048+
{ loading ? ' loading... ' : <input
1049+
disabled={!state.editable}
1050+
value={state.value || ''}
1051+
onChange={mutators.change}
1052+
onBlur={mutators.blur}
1053+
onFocus={mutators.focus}
1054+
/> }
1055+
<span style={{ color: 'red' }}>{state.errors}</span>
1056+
<span style={{ color: 'orange' }}>{state.warnings}</span>
1057+
</React.Fragment>
1058+
}}
1059+
</Field>
1060+
)
1061+
const CheckedField = props => (
1062+
<Field {...props}>
1063+
{({ state, mutators }) => {
1064+
const loading = state.props.loading
1065+
return <React.Fragment>
1066+
{ props.label && <label>{props.label}</label> }
1067+
{ loading ? ' loading... ' : <input type="checkbox" onChange={() => {
1068+
mutators.change(!state.value)
1069+
}} checked={!!state.value} /> }
1070+
<span style={{ color: 'red' }}>{state.errors}</span>
1071+
<span style={{ color: 'orange' }}>{state.warnings}</span>
1072+
</React.Fragment>
1073+
}}
1074+
</Field>
1075+
)
1076+
1077+
const { onFormMount$, onFieldValueChange$ } = FormEffectHooks
1078+
const getEffects = ()=>{
1079+
const actions = createFormActions()
1080+
onFormMount$().subscribe(() => {
1081+
actions.setFieldState('a~', state => state.visible = false)
1082+
})
1083+
1084+
onFieldValueChange$('trigger').subscribe((triggerState) => {
1085+
actions.setFieldState('a~', state => {
1086+
state.visible = triggerState.value
1087+
})
1088+
})
1089+
1090+
onFieldValueChange$('a').subscribe((fieldState) => {
1091+
actions.setFieldState('a-copy', state => {
1092+
state.value = fieldState.value
1093+
})
1094+
})
1095+
}
1096+
1097+
const actions = createFormActions()
1098+
const App = () => {
1099+
return (
1100+
<Form
1101+
actions={actions}
1102+
effects={() => {
1103+
getEffects()
1104+
}}
1105+
>
1106+
<CheckedField name="trigger" label="show/hide" />
1107+
<div>
1108+
<InputField label="a" name="a" />
1109+
</div>
1110+
<div>
1111+
<InputField label="a-copy" name="a-copy" />
1112+
</div>
1113+
</Form>
1114+
)
1115+
}
1116+
1117+
ReactDOM.render(<App />, document.getElementById('root'))
1118+
```
1119+
1120+
10301121
#### combo 字段
10311122

10321123
示例:combo username 和 age 字段, 更多用法,请点击[FormSpy](#FormSpy)查看
@@ -1644,7 +1735,7 @@ const App = () => {
16441735
<Field name="user" initialValue={{}}>
16451736
{({ state, mutator }) => {
16461737
return (
1647-
<VirtualField name="layout">
1738+
<VirtualField name="user.layout">
16481739
{({ state: layoutState }) => {
16491740
return (
16501741
<Layout

packages/react/src/__tests__/useField.spec.tsx

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,13 @@ describe('useField hook',()=>{
4949
return <FormContext.Provider value={form}>{children}</FormContext.Provider>
5050
}
5151

52-
const { result, waitForNextUpdate } = renderHook(() => useField({ name: 'username' }), { wrapper: formWrapper })
52+
const { result } = renderHook(() => useField({ name: 'username' }), { wrapper: formWrapper })
5353
expect(result.current.state.value).toEqual(undefined)
5454
act(() => {
5555
globalForm.setFormState(state => state.values.username = 'abcd')
5656
})
5757

58-
// forceUpdate will trigger in raf, use waitForNextUpdate
59-
expect(result.current.state.value).toEqual(undefined)
60-
await waitForNextUpdate()
61-
expect(result.current.state.value).toEqual('abcd')
58+
expect(result.current.state.value).toEqual('abcd')
6259
})
6360

6461
test('mounted change', async ()=>{
@@ -95,7 +92,7 @@ describe('useField hook',()=>{
9592
editable: true,
9693
rules: [],
9794
}
98-
const { result, waitForNextUpdate, rerender } = renderHook(() => useField(initialProps), { wrapper: formWrapper })
95+
const { result, rerender } = renderHook(() => useField(initialProps), { wrapper: formWrapper })
9996
expect(result.current.props).toEqual({ disabled: true })
10097
expect(result.current.state.required).toEqual(false)
10198
expect(result.current.state.editable).toEqual(true)
@@ -112,7 +109,6 @@ describe('useField hook',()=>{
112109
expect(result.current.state.rules).toEqual([])
113110

114111
rerender()
115-
await waitForNextUpdate()
116112

117113
expect(result.current.props).toEqual(initialProps.props)
118114
expect(result.current.state.required).toEqual(initialProps.required)
@@ -152,19 +148,26 @@ describe('useField hook',()=>{
152148
}
153149

154150
const fieldProps = { name: 'username', required: true }
155-
const { result: result1, waitForNextUpdate: waitForNextUpdate1 } = renderHook(() => useField(fieldProps), { wrapper: formWrapper })
151+
const { result: result1, rerender } = renderHook(() => useField(fieldProps), { wrapper: formWrapper })
156152
expect(result1.current.state.errors).toEqual('')
157153
expect(result1.current.state.value).toEqual(undefined)
158-
result1.current.mutators.change('')
159-
await waitForNextUpdate1()
154+
rerender()
155+
act(() => {
156+
result1.current.mutators.change('')
157+
})
158+
159+
// await waitForNextUpdate1()
160160
expect(result1.current.state.value).toEqual('')
161161
expect(result1.current.state.errors).toEqual('')
162162

163163
const { result: result2, waitForNextUpdate: waitForNextUpdate2 } = renderHook(() => useField({ ...fieldProps, triggerType: 'onChange' }), { wrapper: formWrapper })
164164
expect(result2.current.state.errors).toEqual('')
165165
expect(result2.current.state.value).toEqual(undefined)
166166

167-
result2.current.mutators.change('')
167+
act(() => {
168+
result2.current.mutators.change('')
169+
})
170+
168171
await waitForNextUpdate2()
169172
expect(result2.current.state.value).toEqual('')
170173
expect(result2.current.state.errors).toEqual('This field is required')
@@ -179,16 +182,19 @@ describe('useField hook',()=>{
179182
const fieldProps = { name: 'username', required: true }
180183
const { result: result1, rerender } = renderHook(() => useField(fieldProps), { wrapper: formWrapper })
181184
expect(result1.current.state.errors).toEqual('')
182-
result1.current.mutators.blur()
183185
rerender()
186+
act(() => {
187+
result1.current.mutators.blur()
188+
})
189+
184190
expect(result1.current.state.errors).toEqual('')
185191

186192
const { result: result2, waitForNextUpdate: waitForNextUpdate2 } = renderHook(() => useField({ ...fieldProps, triggerType: 'onBlur' }), { wrapper: formWrapper })
187193
expect(result2.current.state.errors).toEqual('')
188194

189195
act(() => {
190196
result2.current.mutators.blur()
191-
})
197+
})
192198
await waitForNextUpdate2()
193199
expect(result2.current.state.errors).toEqual('This field is required')
194200
})

packages/react/src/__tests__/useVirtualField.spec.tsx

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,12 @@ describe('useVirtualField hook',()=>{
4848
return <FormContext.Provider value={form}>{children}</FormContext.Provider>
4949
}
5050

51-
const { result, rerender } = renderHook(() => useVirtualField({ name: 'username' }), { wrapper: formWrapper })
51+
const { result } = renderHook(() => useVirtualField({ name: 'username' }), { wrapper: formWrapper })
5252
expect(result.current.state.visible).toEqual(true)
5353
act(() => {
5454
globalForm.setFieldState('username', state => state.visible = false)
5555
})
5656

57-
// forceUpdate will trigger in raf, use waitForNextUpdate
58-
expect(result.current.state.visible).toEqual(true)
59-
rerender()
6057
expect(result.current.state.visible).toEqual(false)
6158
})
6259

@@ -91,15 +88,12 @@ describe('useVirtualField hook',()=>{
9188
name: 'username',
9289
props: { disabled: true },
9390
}
94-
const { result, waitForNextUpdate, rerender } = renderHook(() => useVirtualField(initialProps), { wrapper: formWrapper })
91+
const { result, rerender } = renderHook(() => useVirtualField(initialProps), { wrapper: formWrapper })
9592
expect(result.current.props).toEqual({ disabled: true })
9693
initialProps.props = { disabled: false }
9794

9895
expect(result.current.props).toEqual({ disabled: true })
99-
10096
rerender()
101-
await waitForNextUpdate()
102-
10397
expect(result.current.props).toEqual(initialProps.props)
10498
})
10599
})

0 commit comments

Comments
 (0)