Home > Net >  Getting custom components to work within react-hook-form using onChange
Getting custom components to work within react-hook-form using onChange

Time:12-03

I have a checkout cart where you have different cart items, and for each one you can change the quantity prior to purchase.

Here's how the code looks:

import React, { useEffect, useState } from "react";

import PureInput from "./PureInput";
import { useForm, Controller } from "react-hook-form";

const CartInner = React.forwardRef(
  (
    {
      control,
      element,
      value,
      handleOnChange,
      images,
      name,
      monthlyAmount,
      price,
      projectedGrowth,
      id,
      ...inputProps
    }: any,
    ref: any
  ) => {
    return (
      <div className="grid gap-8 grid-cols-2 mb-12 py-6 px-8 border-2 border-slate-200">
        <div>
          <PureInput
            min={200}
            max={price}
            onChange={handleOnChange}
            type="number"
            step={200}
            defaultValue={element.price}
            id={id}
            ref={ref}
            {...inputProps}
          />
        </div>
      </div>
    );
  }
);

export default function Checkout() {
  const { control, handleSubmit } = useForm();

  const handleOnChange = (index: any, e: any) => {
    console.log(e, "e");
  };

  const onSubmit = async (data: any) => {
    console.log(data, "data from Form.tsx");
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)} className="grid gap-8 grid-cols-3">
      <div className="col-span-2">
        {[0, 2].map((element, index) => {
          return (
            <fieldset key={index}>
              <Controller
                render={({ field }) => (
                  <CartInner
                    element={element}
                    handleOnChange={(e) => handleOnChange(index, e)}
                    {...field}
                  />
                )}
                name={`test.${index}.lastName`}
                control={control}
              />
            </fieldset>
          );
        })}
        <button>Progess to payment</button>
      </div>
    </form>
  );
}

And the PureInput:

import * as React from "react";

type IProps = any;

const PureInput = React.forwardRef(
  ({ className, id, onChange, ...inputProps }: IProps, ref: any) => {
    return (
      <input
        id={id}
        ref={ref}
        onChange={onChange}
        type="input"
        className={`${className} block w-full bg-white text-black rounded-md border-2 font-bold border-grey-200 text-xl px-4 py-4 focus:border-orange-500 focus:ring-orange-500`}
        {...inputProps}
      />
    );
  }
);

export default PureInput;

Everything works fine in terms of submitting the form. When I do, I get an array of whatever values I have entered into the input:

[{lastName: "1600"}
{lastName: "800"}]

My package versions:

"react-dom": "18.2.0",
"react-hook-form": "^7.29.0",

But my onChange no longer fires. How can I get the onChange to fire so I can log the value of the input inside <Checkout /> component?

Here's a codesandbox if it helps

CodePudding user response:

The onChange event handler is only called when the input value is changed. It's not called when the form is submitted.

To access the values of the inputs when the form is submitted, you can use the handleSubmit hook provided by react-hook-form in your Checkout component. This hook takes a callback function as an argument, and this function will be called when the form is submitted. The values of the form inputs will be passed to this callback as an object:

import React, { useEffect, useState } from "react";
import { useForm } from "react-hook-form";

const CartInner = React.forwardRef(
  (
    {
      control,
      element,
      value,
      handleOnChange,
      images,
      name,
      monthlyAmount,
      price,
      projectedGrowth,
      id,
      ...inputProps
    }: any,
    ref: any
  ) => {
    return (
      <div className="grid gap-8 grid-cols-2 mb-12 py-6 px-8 border-2 border-slate-200">
        <div>
          <PureInput
            min={200}
            max={price}
            onChange={handleOnChange}
            type="number"
            step={200}
            defaultValue={element.price}
            id={id}
            ref={ref}
            {...inputProps}
          />
        </div>
      </div>
    );
  }
);

export default function Checkout() {
  const { control, handleSubmit } = useForm();

  const handleOnChange = (index: any, e: any) => {
    console.log(e, "e");
  };

  const onSubmit = async (data: any) => {
    console.log(data, "data from Form.tsx");
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)} className="grid gap-8 grid-cols-3">
      <div className="col-span-2">
        {[0, 2].map((element, index) => {
          return (
            <fieldset key={index}>
              <Controller
                render={({ field }) => (
                  <CartInner
                    element={element}
                    handleOnChange={(e) => handleOnChange(index, e)}
                    {...field}
                  />
                )}
                name={`test.${index}.lastName`}
                control={control}
              />
            </fieldset>
          );
        })}
        <button>Progess to payment</button>
      </div>
    </form>
  );
}

CodePudding user response:

This part of the documentation led me to the answer:

import React, { useEffect, useState } from "react";

import PureInput from "./PureInput";
import { useForm, Controller } from "react-hook-form";

const CartInner = React.forwardRef(
  ({ onChange, onBlur, name, label, ...inputProps }: any, ref: any) => {
    return (
      <input
        name={name}
        ref={ref}
        onChange={onChange}
        onBlur={onBlur}
        type="number"
      />
    );
  }
);

export default function Checkout() {
  const { control, handleSubmit } = useForm();

  const handleOnChange = (index: any, e: any) => {
    console.log(e.target.value, "e");
  };

  const onSubmit = async (data: any) => {
    console.log(data, "data from Form.tsx");
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)} className="grid gap-8 grid-cols-3">
      <div className="col-span-2">
        {[0, 2].map((element, index) => {
          return (
            <fieldset key={index}>
              <Controller
                render={({ field: { onBlur, value, name, ref } }) => (
                  <CartInner
                    key={index}
                    name={name}
                    ref={ref}
                    onChange={(e) => handleOnChange(index, e)}
                    onBlur={onBlur}
                  />
                )}
                name={`test.${index}.lastName`}
                control={control}
              />
            </fieldset>
          );
        })}
        <button>Progess to payment</button>
      </div>
    </form>
  );
}

// add delete
// total money
// add the cart documents to a history with a timestamp and show it was a BUY ORDER
// delete the documents from the cart
  • Related