Closed meza closed 1 year ago
Hi @meza, I noticed you are using a custom intent (NEWSLETTER_INTENT
). How are you configuring the submit button? It would be even better if you can show me the whole component with your newsletter form.
It's a bit difficult to show because it's all out in different components + in a private repo but here's the button:
import { useEffect, useState } from 'react';
import { conform } from '@conform-to/react';
import classNames from 'classnames';
export interface SubmitButtonProps {
intent: string;
className?: string;
disabled?: boolean;
}
export const SubmitButton = (props: SubmitButtonProps) => {
const [disabled, setDisabled] = useState(props.disabled || false);
useEffect(() => {
setDisabled(props.disabled || false);
}, [props.disabled]);
return (
<button className={classNames('primary', props.className)} type='submit' name={conform.INTENT} value={props.intent} disabled={disabled}>Submit</button>
);
};
The input as follows:
export interface InputProps {
label?: string;
placeholder: string;
conformField: FieldConfig;
tabIndex?: number | undefined;
type?: HTMLInputTypeAttribute | undefined;
value?: string | undefined;
}
export const Input = (props: InputProps) => {
const finalType = props.type || 'text';
const id = useId();
const errorId = useId();
const { t } = useTranslation();
return (
<div className={classNames('input')}>
<input
className={classNames({ 'error': props.conformField.error !== undefined })}
{...conform.input(props.conformField, { type: finalType })}
id={id}
placeholder={' '}
tabIndex={props.tabIndex}
aria-invalid={props.conformField.error === undefined ? 'false' : 'true'}
aria-required={props.conformField.required === undefined ? false : props.conformField.required}
aria-describedby={errorId}
/>
{props.label && <label htmlFor={id} className={classNames({ 'required': props.conformField.required })}>{props.label}</label>}
<div id={errorId} role='alert' className={classNames('input-error')} aria-live={'polite'}>{props.conformField.error ? t(props.conformField.error) : ''}</div>
</div>
);
};
And the form is:
<newsletter.Form
className={classNames('newsletter', { 'visible': true })}
method={'POST'} {...form.props}>
<Input label={'What is your email address?'} type={'email'} tabIndex={3} conformField={email}/>
<div className={'button'}>
<SubmitButton disabled={!!email.error || (newsletter.state === 'submitting')} intent={NEWSLETTER_INTENT}/>
</div>
</newsletter.Form>
It looks like there is a bug on the revalidation logic currently which tries to validate the submit button š , very likely when you unfocus the submit button (i.e. onBlur). But this still doesn't answer why you saw the request made there with client validation in place.
The only exception I can think of is if it throws an error during validation and so Conform fallbacks to server validation. Can you verify this? (e.g. Conform will print out the error if onValidate
throws an error, or you can wrap it with a try-catch block)
@edmundhung about to test! Been tied up with other things but let's experiment!
It looks like there is a bug on the revalidation logic currently which tries to validate the submit button š , very likely when you unfocus the submit button (i.e. onBlur). But this still doesn't answer why you saw the request made there with client validation in place.
The only exception I can think of is if it throws an error during validation and so Conform fallbacks to server validation. Can you verify this? (e.g. Conform will print out the error if
onValidate
throws an error, or you can wrap it with a try-catch block)
Verified it, there's no validation error.
Any other ideas I could try?
I also see the double submission on the server side
Obviously the first one is correct and then the second one fails because I don't have that intent
It would appear that it's all coming from the onBlur. When I change it to onInput, it doesn't do the double post. It however doesn't clear out the submitted field but that might just be user error :D
Hi @meza, sorry for the late reply. I was busy with Remix Conf for the last couple weeks.
I have a patch ready that avoids the extra validation caused by the button and it will be released by the end of this week. But I still believe there is an unknown issue in your case as I wouldn't expect it doing a server validation with client validation setup. Any chance you can provide a sandbox that reproduce the issue? You can fork the remix example here.
This should be fixed on v0.6.2.
Using Remix, I'm finding a weird behaviour for a simple newsletter form.
When I submit, I get this:
The first call behaves as expected
It does a redirect('/');
The next invocation however looks like this:
This obviously fails on the server that's expecting a different intent, thus renders the form invalid - returning the data, filling out the form.
What am I doing wrong? Where does the validate/intent come from?
My form config:
Some of the other bits: