I am working in a project using react with typescript, and I'm using react-hook-form and the form component that comes with shadcn/ui: https://ui.shadcn.com/docs/components/input#form For my case, I created a custom Input component which has a button inside of it (with a pen icon), which will toggle on and off the **readonly **property of the input, but i want to add another functionality:
- When the user clicks on the pen button to modify the value of the input, I want to replace that button with a submit button once the initial value of that input changes, and only when he clicks on the submit button and the form submits can the input return to being **readonly **again and so on for every input.
The problem is that the onChange property doesn't work on the input to get the value or just to set a state indicating that the value has been changed to replace the buttons, and when I tried to isolate the input by using react-hook-form's hooks such as useFormContext to get the **control ** prop and then pass it as well as the name of the input to the useWatch or useFormState, it triggers countless re-renders. If anyone has any idea or suggestion please help me. Here is the code of my component if anyone wants to modify it:
import * as React from "react";
import { Button } from "@/components/ui/button";
import { Input, type InputProps } from "@/components/ui/input";
import { cn } from "@/lib/utils";
import { Pencil, PencilOff } from "lucide-react";
interface EditableInputProps extends InputProps {
editPermission?: boolean;
}
const EditableInput = React.forwardRef<HTMLInputElement, EditableInputProps>(
({ className, editPermission = true, ...props }, ref) => {
const [canEdit, setCanEdit] = React.useState<boolean>(false);
const disabled = props.disabled || !editPermission;
const handleEdit = () => {
if (!disabled) {
setCanEdit((prev) => !prev);
}
};
return (
<div className="relative">
<Input
ref={ref}
type="text"
readOnly={!canEdit ? true : false}
className={cn("pr-10", className)}
{...props}
/>
<Button
type="button"
variant="ghost"
size="sm"
className="absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent"
disabled={disabled}
onClick={() => handleEdit()}
>
{canEdit && !disabled ? (
<Pencil className="h-4 w-4" aria-hidden="true" />
) : (
<PencilOff className="h-4 w-4" aria-hidden="true" />
)}
<span className="sr-only">
{canEdit ? "Can edit input" : "Cannot edit input"}
</span>
</Button>
</div>
);
}
);
EditableInput.displayName = "EditableInput";
export { EditableInput };
// usage in my form
<FormField
control={form.control}
name="order_total"
render={({ field }) => (
<FormItem>
<FormLabel>
Order Total
</FormLabel>
<FormControl>
<EditableInput
{...field}
placeholder="Order Total"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>