Can't get formik validation to work with react-router - reactjs

Good morning,
I want a react-router link to forward ONLY when the form has no errors, unfortunately the code that's below does forward before the form is even validated so no error shows up and so on. Can you please guide me on how to fix this, so that the form is validated and if it has no errors, the link forwards further?
Here's my code:
<Link to={this.props.formikProps.isValid?'mainMenu': 'itemDetail'}>{!this.props.disabled && <button className="buttons-panel__button" type="button" onClick={() => (this.props.handleSubmit(false), this.props.formikProps.isValid? '' : this.onValidationFail())} >Save</button>}</Link>
The method is(it's basically only to display a popup error, and it's working):
onValidationFail() {
setAlertMessage("You need to fill all mandatory fields")
}
So far the best I've achieved is that it works as intended in every case, but if the form hasn't been touched at all. I've tried several different ways, including formValidate from formik, for some reason it didn't work too.
I'm looking forward to some tips/hints/solutions on the case.

I think that your solution looks bit overcomplicated. Maybe just use simple form submit, and after form submission redirect user to route.
It would look like that:
import { Formik, Form, Field } from 'formik'
class MyForm {
render() {
return (
<Formik
initialValues={...}
onSubmit={() => {this.props.history.push('/go-somewhere')}}
>
<Form>
<Field name="firstName" type="text" />
<button type="submit">Submit</button>
</Form>
</Formik>
)
}
}

Related

Full stack components in Remix

In this video, Ryan creates a newsletter signup form. The code for both the frontend and the backend are in the same file, which is an awesome Remix feature. However, something like a newsletter signup form might be on several pages of a website/app.
If you merely import the newsletter component on the homepage, for example, it will try to post to the homepage, which doesn't have an action() that handles that data submittion.
<Form replace method="post" aria-hidden={state === 'success'}>
<h2>Subscribe!</h2>
<p>Don't miss any of the action!</p>
<fieldset disabled={state === 'submitting'}>
<input aria-label="Email address" aria-describedby="error-message" ref={inputRef} {...formDefinition.inputs.email.validationAttrs} name="email" placeholder="you#example.com" defaultValue={actionData?.fields?.email} />
<button type="submit">{state === 'submitting' ? 'Subscribing...' : 'Subscribe'}</button>
</fieldset>
<p id="error-message">{state === 'error' ? actionData?.message : <> </>}</p>
</Form>
We can fix this by adding action="/newsletter" to the <Form>:
<Form replace method="post" aria-hidden={state === 'success'}>
But then, when you submit the form, you'll actually navigate to the /newsletter route, because data mutations, including form submitions are modeled with navigation. To fix this we could replace <Form> and useActionData() with const fetcher = useFetcher(), <fetcher.Form> and fetcher.data.
But now we lose the ability to merely reset the newsletter form with a simple <Link to=".">Start over</Link>.
Is this the right way of creating and sharing full stack components across routes in Remix?

React hook form + Modal + data-bs-dismiss

I want to use a tailwind modal, who open & close with data-bs-dismiss argument.
I put on my button : data-bs-dismiss='modal'
But with react hook form, it's doesn't work like i want. If the form contains error or bad input, the data-bs-dismiss was execute and not payed attention to the form errors.
I want close the modal & submit, when the form validation is ok, and contains no other error.
If anyone have a solution.. thank's
My code :
const methods = useForm()
return(
<FormProvider {...methods}>
<form id={props.id} onSubmit={methods.handleSubmit(onSubmit)}>
<input id='...'> ... <input/>
<input id='...'> ... <input/>
<input id='...'> ... <input/>
...
<li>
<Button
btnId={`${props.btnContent}Btn`}
btnContent={props.btnContent}
btnClass='btn'
btnType='submit'
btnRipple
dismiss={'modal'}
form={props.id}
/>
<li/>
<form/>
</FormProvider>
)
Well, what you can do is, Instead of using dismiss you can check in your onSubmit function for any errors. And if the form has any errors then keep the modal open otherwise submit the form and close it.

Browser autocomplete issue

I am having a hard time trying to find a proper way to disable autocomplete from browser in antd forms.
In normal form inputs
<Input autoComplete="none" />
works perfectly.
the problem is that in Select and Autocomplete components it does not work, it does not accept this prop, the question is:
is it posible to disable browser autocomplete in the whole form?, I have been browin for hours for an answer to this issue, and so far all I found is that it is not posible, but just want to give it last try asking here.
Thanks a lot
I guess there is, Set autoComplete 'off' to the entire form.
const handleSubmit(e) {
e.preventDefault();
//... Submit Actions
}
return (
<form autoComplete='off' onSubmit={handleSubmit}>
// ... form contents ...
</form>
)

Convenient way to mark a complete form as readonly

I have an antd form with multiple form items and try to find a way to mark the complete form as readonly. I could for sure set each input component to 'disabled' but I wonder if there is a convenient way to do so on the form via an API call that I do not know yet.
Wrapping the antd form inside a fieldset and setting this to 'disabled' works pretty well.
<fieldset disabled={editorDisabled}>
<Form>
...
<Form/>
<fieldset/>
I don't see such an option in the form api, and I think it's the rare use case, so I doubt it exists. However, you can simply add variable which will track the disabled status, i.e.:
const YourAwesomeComponent = (props) => {
const disabled = someLogicToCalculateTheDisabledStatus(props);
return <Form ...>
<Input disabled={disabled} ... />
<Select disabled={disabled} ... />
<Button disabled={disabled} ... />
</Form>
}
Hope it helps.
As of version 4.21.0 (Jun 6, 2022) the disabled prop can be used in the form to disable all fields, i.e.:
<Form disabled={true}>
...
</Form>
It is enforced as long as a <Form.Item/> isn't explicitly marked as not disabled with disabled={false}. You can see the reference in antd docs here.

Is there a way to get access to redux-form errors from inside the Container with reduxForm V7?

Previously, in Redux-Form V5, I was accessing errors as email.error and password.error from inside the render function.
In V7, my CSS is giving me grief to do it this way:
const renderInput = (field) => {
const { meta: { touched, error } } = field
return (
<div>
<input type={field.type} placeholder={field.placeholder} {...field.input} />
<div className="inputErrorText">
{touched ? error : ''} // this doesnt work due to my CSS
</div>
</div>
)
}
// ...inside the Container:
<div className="ui left icon input">
<i className="user icon"></i>
<Field
name="email"
component={renderInput}
type="text"
placeholder="Enter your Email"
/>
</div>
// Below is the old way I was doing it
{/*email.touched && email.error && <div className="inputErrorText">{email.error}</div>*/}
If I render the errors via renderInput(), the CSS gets mangled due to what looks like some position relative+absolute reasons, so I need the errors to render below that last div at the bottom of my code above.
How can I deal with rendering the field errors outside renderInput() and/or elsewhere in the <form>. I think my question centers around the field input parameter of renderInput(). How to access it inside the Container? Is it available on this.props anywhere?
I just got it working. I found this source: Redux-form 6.0.0 access error outside Field component which illustrates a way to use the Fields component of redux-form V6 & 7.
I wasn't able to get it working as shown there. I'm not entirely sure at this point, but it was throwing all kinds of "cannot read property touched of undefined". I tried many combinations of if (!fields) return else destructure, and about 20 different combinations and I just couldn't get it to iterate through using Object.keys(). I was able to console log easily fields.email.input and see everything, but as soon as I started digging into meta, it started throwing undefined even while it was logging the values sometimes. The weird thing is untouched was always false not undefined, so it was fairly infuriating, but I digress.
Another person may have more luck with that above solution. Fields is definitely what a person wants to use to gain control over errors in Redux-form V6-7.
I did however, get it to work like this:
import { reduxForm, Field, Fields } from 'redux-form'
const fieldNames = [
'email',
'password'
]
const renderInput = (field) => {
return (
<input type={field.type} placeholder={field.placeholder} {...field.input} />
)
}
// inside the Container (class methods)
renderEmailError({ email }) {
const { touched, error } = email.meta
if (touched && error) return <div className="inputErrorText">{error}</div>
return null
}
renderPasswordError({ password }) {
const { touched, error } = password.meta
if (touched && error) return <div className="inputErrorText">{error}</div>
return null
}
// Inside render function
<div className="field">
<div className="ui left icon input">
<i className="user icon"></i>
<Field
name="email"
component={renderInput}
type="text"
placeholder="Enter your Email"
/>
</div>
<Fields names={fieldNames} component={this.renderEmailError} />
</div>
x
I was not able to get it working with the "standard" implementation using Field because my CSS simply does not allow for a div to be placed along with the input. I could not alter the CSS because of the way it places the icons on top of the input fields.
I experimented with moving the entire 'fieldset' into the renderInput() function, but I became equally stuck because of the onClick handler when you click the eyeball to expose the password (UX substitute for passwordConfirm field). I was not able to update the component state from outside the class, nor was I able to call the function properly from outside the class.
Additionally, I also learned that you cannot manipulate an input's type if it is password using document.querySelector('input').type because it is a security risk.

Resources