Here, I attempted to create a custom component that can receive data through props and function as a basic form input.
However, I encountered an issue where errors are not being displayed. Can someone please explain what mistake I might have made in my implementation?
If anyone has the knowledge, I would greatly appreciate your assistance in resolving this issue.
trying to build an reuseable component but now working why??
/* eslint-disable react/prop-types */
const Input = (props) => {
const {formik, icon, title,type,otherState,btn1,btn2, name} = props
Input.defaultProps = {
type: "text",
otherState: null,
btn1: null,
btn2: null,
name: title.toLowerCase()
}
if (otherState) {
var {state, setState} = otherState
}
if (btn1) {
var {icon1, text1, action1} = btn1
}
if (btn2) {
var {icon2, text2, action2} = btn2
}
return (
<div className="mb-2">
<span className="flex">
<img className="w-6 h-6 mr-2" src={icon} alt={title} />
<label
htmlFor={name}
className="block text-sm font-semibold text-textPrimary dark:text-dark_textPrimary"
>
{title}
</label>
</span>
<span className="flex w-full">
{console.log(formik.touched.name)}
<input
type={type}
name={name}
value={formik.values.name}
title={`${formik.touched.name && formik.values.name == "" ? `${title} is required` : ""}`}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
placeholder={
formik.touched.name && formik.errors.name
? `${title} is required`
: `${title}`
}
className={`block w-full px-4 py-2 mt-2 text-textPrimary dark:text-dark_textPrimary bg-background dark:bg-dark_background border-2 rounded-md focus:ring-secondary ${
formik.touched.name && formik.errors.name
? " border-error_red placeholder-error_red"
: "border-cardBorder dark:border-dark_cardBorder"
}`}
/>
{
btn1 || btn2 ? (
<img
className="w-6 h-7 cursor-pointer mt-3 transition duration-500 ease-in-out transform hover:-translate-y-1 hover:scale-110"
onClick={() => action1()}
src={state ? icon1 : icon2}
title={!state ? text1 : text2}
/>) : null
}
</span>
{
formik.touched.name && formik.errors.name && formik.values.name != "" ? (
<h3 className="text-xs text-error_red">{formik.errors.name}</h3>
) : null
}
</div>
)
}
export default Input
Here is a Input component, That first components for email and second for password
<Input
formik={formik}
icon={emailIcon}
title="Email"
name="email"
type="email"
/>
<Input
formik={formik}
icon={passwordIcon}
name="password"
title="Password"
type={showPassword ? "password" : "text"}
otherState={{'state':showPassword, 'setState':setShowPassword}}
btn1={{'icon1':eyeOpenIcon, 'text1':"Hide Password", 'action1':() => setShowPassword(!showPassword)}}
btn2={{'icon2':eyeCloseIcon, 'text2':"Show Password", 'action2':() => setShowPassword(!showPassword)}}
/>
To access the value or the error related to a given input, you should use its name. Since inside your Input component, you are getting the name as part of a name prop, you would do like this:
formik.values[name] // To access its value
formik.touched[name] // To check if it's checked
formik.errors[name] // To gets its related error
Formik has tons of different examples you can use on their GitHub and documentation.
Rather than spin up your own, you could potentially build off one of their examples so to speak.
For example, there is this example which gives immediate feedback for validation on the fields. Take a look at this codesandbox and code below:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { useFormik, FormikProvider, Form, useField } from 'formik';
import './styles.css';
import * as Yup from 'yup';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const TextInputLiveFeedback = ({ label, helpText, ...props }) => {
const [field, meta] = useField(props);
// Show inline feedback if EITHER
// - the input is focused AND value is longer than 2 characters
// - or, the has been visited (touched === true)
const [didFocus, setDidFocus] = React.useState(false);
const handleFocus = () => setDidFocus(true);
const showFeedback =
(!!didFocus && field.value.trim().length > 2) || meta.touched;
return (
<div
className={`form-control ${
showFeedback ? (meta.error ? 'invalid' : 'valid') : ''
}`}
>
<div className="flex items-center space-between">
<label htmlFor={props.id}>{label}</label>{' '}
{showFeedback ? (
<div
id={`${props.id}-feedback`}
aria-live="polite"
className="feedback text-sm"
>
{meta.error ? meta.error : '✓'}
</div>
) : null}
</div>
<input
{...props}
{...field}
aria-describedby={`${props.id}-feedback ${props.id}-help`}
onFocus={handleFocus}
/>
<div className="text-xs" id={`${props.id}-help`} tabIndex="-1">
{helpText}
</div>
</div>
);
};
const Example = () => {
const formik = useFormik({
initialValues: {
username: '',
},
onSubmit: async (values) => {
await sleep(500);
alert(JSON.stringify(values, null, 2));
},
validationSchema: Yup.object({
username: Yup.string()
.min(8, 'Must be at least 8 characters')
.max(20, 'Must be less than 20 characters')
.required('Username is required')
.matches(
/^[a-zA-Z0-9]+$/,
'Cannot contain special characters or spaces'
),
}),
});
return (
<FormikProvider value={formik}>
<Form>
<TextInputLiveFeedback
label="Username"
id="username"
name="username"
helpText="Must be 8-20 characters and cannot contain special characters."
type="text"
/>
<div>
<button type="submit">Submit</button>
<button type="reset">Reset</button>
</div>
</Form>
</FormikProvider>
);
};
ReactDOM.render(
<div className="app">
<h1 className="text-4xl">Accessible instant feeback with Formik 2</h1>
<div className="example">
<Example />
</div>
</div>,
document.getElementById('root')
);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With