skip to Main Content

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)}}
       

          />

2

Answers


  1. 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
    
    Login or Signup to reply.
  2. 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')
    );
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search