Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(form): unexpected validate error before unRegister #1752

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 39 additions & 2 deletions packages/semi-ui/form/__test__/field.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { Form, Select } from '../../index';
import { noop } from 'lodash';
import { func } from 'prop-types';
import { BASE_CLASS_PREFIX } from '../../../semi-foundation/base/constants';
import { sleep as baseSleep } from '../../_test_/utils/index';

Expand Down Expand Up @@ -459,6 +457,45 @@ describe('Form-field', () => {
expect(formApi.getError('text')).not.toBeUndefined();
});

it('validate before unRegister', async () => {
let formApi = null;

const Foo = () => (
<Form
initValues={{
text: 'semi',
}}
getFormApi={api => {
formApi = api;
}}
>
{({ formState }) => (
<>
{!!formState.values.text && (
<Form.Input
field="text"
rules={[
{ required: true },
]}
/>
)}
</>
)}
</Form>
);

const form = mount(<Foo />);
const event = { target: { value: '' } };
form.find(`.${BASE_CLASS_PREFIX}-input`).simulate('change', event);
await sleep(200);
form.update();

formApi.setValue('text', 'foo');
expect(await formApi.validate()).toEqual({
text: 'foo',
});
});

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个 Demo 我有点疑问哈,跟 description里描述的场景似乎不太一样。

这个test case的逻辑,我理解的是

  1. 初始 Field Input 有值 'semi'
  2. 然后将值删除成空字符串,此时触发了校验
  3. 然后再使用 formApi.setValue 设成另外一个值
  4. 希望 error 不存在?
() => {
    const formRef = useRef();
    const change = () => {
        formRef.current.formApi.setValue('text', 'foo');
    };
    return (
        <Form
            initValues={{
                text: 'semi',
            }}
            ref={formRef}
        >
            {({ formState }) => (
                <>
                    {!!formState.values.text && (
                        <Form.Input
                            field="text"
                            rules={[
                                { required: true },
                            ]}
                        />
                    )}
                    <Button onClick={() => change()}>change</Button>
                </>
            )}
        </Form>
    )
}   

20230814165234_rec_

// TODO
// it('allowEmptyString', () => {});
// it('extraText')
Expand Down
12 changes: 12 additions & 0 deletions packages/semi-ui/form/hoc/withField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ function withField<
setStatus('default');
};


const unmounted = useRef(false);
useEffect(() => () => {
unmounted.current = true;
}, []);

// Execute the validation rules specified by rules
const _validateInternal = (val: any, callOpts: CallOpts) => {
let latestRules = rulesRef.current || [];
Expand All @@ -196,6 +202,9 @@ function withField<
if (validatePromise.current !== rootPromise) {
return;
}
if (unmounted.current) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

但这里的改动,我看着像是为了解决 组件触发校验后(例如 async 的validate),如果最终结果return时,field已经被卸载了,就不再消费 error。

跟【动态删除表单项后,再次增加该表单项目时,存在校验错误】严格意义上也不是一码事?

return;
}
// validation passed
setStatus('success');
updateError(undefined, callOpts);
Expand All @@ -205,6 +214,9 @@ function withField<
if (validatePromise.current !== rootPromise) {
return;
}
if (unmounted.current) {
return;
}
let { errors, fields } = err;
if (errors && fields) {
let messages = errors.map((e: any) => e.message);
Expand Down