How to create two reusable invoking custom hook?

Viewed 129

I created some logic that is reusable code anywhere. I have an input.jsx component for handling input elements, and use-form.js custom hook for managing all of the stuff and I have a basic-form component for creating form logic. Inside of basic-form component, I'm calling the custom hook many times. That is tedious and quite repetitive* so I wanna create two reusable invoking custom hooks in the input.jsx component

input.component.jsx

const Input = (props) => {

return (
<div className={`form-control ${props.error && 'invalid'}`}>
  <label htmlFor={props.id}>{props.label}</label>
  <input
    type={props.type}
    id={props.id}
    value={props.value}
    onChange={props.changeHandler}
    onBlur={props.blurHandler}
  />
  {props.error && <p className="error-text">{props.label} must be entered!</p>}
</div>


 );
};

use-input.js

import { useReducer } from 'react';

const inputReducer = (state, action) => {
  switch (action.type) {
    case 'INPUT':
      return { value: action.value, isTouched: state.isTouched };
    case 'BLUR':
      return { isTouched: true, value: state.value };
    case 'RESET':
      return { value: '', isTouched: false };
  }
  return state;
};

export const useInput = (validateValue) => {
  const [state, dispatch] = useReducer(inputReducer, {
    value: '',
    isTouched: false,
  });

  const valueIsValid = validateValue(state.value);
  const error = !valueIsValid && state.isTouched;

  const valueChangeHandler = (e) => {
    dispatch({ type: 'INPUT', value: e.target.value });
  };

  const inputBlurHandler = () => {
    dispatch({ type: 'BLUR' });
  };

  const reset = () => {
    dispatch({ type: 'RESET' });
  };

  return {
    value: state.value,
    isValid: valueIsValid,
    error,
    valueChangeHandler,
    inputBlurHandler,
    reset,
  };
};

basic-form.component.jsx

import { useInput } from '../hooks/use-input';
import Input from './input.component';

const BasicForm = () => {
  const {
    value: name,
    isValid: nameIsValid,
    error: nameHasError,
    valueChangeHandler: nameChangeHandler,
    inputBlurHandler: nameBlurHandler,
    reset: nameReset,
  } = useInput((value) => value.trim() !== '');

  const {
    value: surname,
    isValid: surnameIsValid,
    error: surnameHasError,
    valueChangeHandler: surnameChangeHandler,
    inputBlurHandler: surnameBlurHandler,
    reset: surnameReset,
  } = useInput((value) => value.trim() !== '');

  const {
    value: email,
    isValid: emailIsValid,
    error: emailHasError,
    valueChangeHandler: emailChangeHandler,
    inputBlurHandler: emailBlurHandler,
    reset: emailReset,
  } = useInput((value) => /^\S+@\S+\.\S+$/.test(value));

  let formIsValid = false;

  if (nameIsValid && surnameIsValid && emailIsValid) {
    formIsValid = true;
  }

  const formSubmitHandler = (e) => {
    e.preventDefault();
    nameReset();
    surnameReset();
    emailReset();
  };

  return (
    <form onSubmit={formSubmitHandler}>
      <div className="control-group">
        <Input
          id="name"
          label="First Name"
          type="text"
          error={nameHasError}
          value={name}
          changeHandler={nameChangeHandler}
          blurHandler={nameBlurHandler}
        />
        <Input
          id="surname"
          label="Last Name"
          type="text"
          error={surnameHasError}
          value={surname}
          changeHandler={surnameChangeHandler}
          blurHandler={surnameBlurHandler}
        />
        <Input
          id="email"
          label="Email"
          type="email"
          error={emailHasError}
          value={email}
          changeHandler={emailChangeHandler}
          blurHandler={emailBlurHandler}
        />
      </div>
      <div className="form-actions">
        <button disabled={!formIsValid}>Submit</button>
      </div>
    </form>
  );
};

export default BasicForm;

The problem with that is we must return two values from input.jsx to basic-form.jsx. The first value shows the property isValid or not. Second, a function for reseting every individual input element. Also, we must receive a function that validates the logic from the parent to the children components. How can we solve this problem? I think we should use useRef, useImperativeHandle and forwardRef but how?

Please vote up for more people to see.

0 Answers
Related