modfified TagInput for prevent multiple rendering

This commit is contained in:
Pramod Mahajan 2025-05-18 02:14:40 +05:30
parent c2ead3cd0f
commit 28a4f63d10

View File

@ -1,30 +1,28 @@
import { useFormContext } from "react-hook-form";
import { useFormContext, useWatch } from "react-hook-form";
import React, { useEffect, useState } from "react";
import { useDispatch } from "react-redux";
import { changeMaster } from "../../slices/localVariablesSlice";
const TagInput = ({
label = "Tags",
name = "tags",
placeholder = "Start typing to add...",
placeholder = "Start typing to add... like employee, manager",
color = "#e9ecef",
options = [],
}) => {
const [tags, setTags] = useState([]);
const [input, setInput] = useState("");
const [suggestions, setSuggestions] = useState([]);
const { setValue, trigger } = useFormContext();
const dispatch = useDispatch();
const { setValue, trigger, control } = useFormContext();
const watchedTags = useWatch({ control, name });
useEffect(() => {
setValue(
name,
tags.map((tag) => ({
id: tag.id ?? null,
name: tag.name,
}))
);
}, [ tags, name, setValue ] );
useEffect(() => {
if (
Array.isArray(watchedTags) &&
JSON.stringify(tags) !== JSON.stringify(watchedTags)
) {
setTags(watchedTags);
}
}, [JSON.stringify(watchedTags)]);
useEffect(() => {
if (input.trim() === "") {
@ -32,29 +30,34 @@ const TagInput = ({
} else {
const filtered = options?.filter(
(opt) =>
opt?.name?.toLowerCase()?.includes(input?.toLowerCase()) &&
!tags?.some((tag) => tag?.name === opt?.name)
opt?.name?.toLowerCase()?.includes(input.toLowerCase()) &&
!tags?.some((tag) => tag.name === opt.name)
);
setSuggestions(filtered);
}
}, [input, options, tags]);
const addTag = async ( tagObj ) =>
{
if (!tags.some((tag) => tag.id === tagObj.id)) {
const cleanedTag = {
id: tagObj.id ?? null,
name: tagObj.name,
};
const newTags = [...tags, cleanedTag];
setTags(newTags);
setValue(name, newTags, { shouldValidate: true }); // only id + name
await trigger(name);
setInput("");
setSuggestions([]);
}
};
const addTag = async (tagObj) => {
if (!tags.some((tag) => tag.name === tagObj.name)) {
const cleanedTag = {
id: tagObj.id ?? null,
name: tagObj.name,
};
const newTags = [...tags, cleanedTag];
setTags(newTags);
setValue(name, newTags, { shouldValidate: true });
await trigger(name);
setInput("");
setSuggestions([]);
}
};
const removeTag = (indexToRemove) => {
const newTags = tags.filter((_, i) => i !== indexToRemove);
setTags(newTags);
setValue(name, newTags, { shouldValidate: true });
trigger(name);
};
const handleInputKeyDown = (e) => {
if (e.key === "Enter" && input.trim() !== "") {
@ -69,7 +72,7 @@ const TagInput = ({
name: input.trim(),
description: input.trim(),
};
addTag(newTag); // Call async function (not awaiting because it's UI input)
addTag(newTag);
} else if (e.key === "Backspace" && input === "") {
setTags((prev) => prev.slice(0, -1));
}
@ -79,13 +82,6 @@ const TagInput = ({
addTag(suggestion);
};
const removeTag = (indexToRemove) => {
const newTags = tags.filter((_, i) => i !== indexToRemove);
setTags(newTags);
setValue(name, newTags, { shouldValidate: true });
trigger(name);
};
const backgroundColor = color || "#f8f9fa";
const iconColor = `var(--bs-${color})`;
@ -120,6 +116,7 @@ const TagInput = ({
/>
</span>
))}
<input
type="text"
value={input}
@ -132,17 +129,17 @@ const TagInput = ({
flex: 1,
minWidth: "120px",
}}
onFocus={() => dispatch(changeMaster("Contact Tag"))}
/>
</div>
{suggestions.length > 0 && (
<ul
className="list-group position-absolute mt-1 bg-white w-50 shadow-sm"
className="list-group position-absolute mt-1 bg-white w-50 shadow-sm "
style={{
zIndex: 1000,
maxHeight: "150px",
overflowY: "auto",
boxShadow:"0px 4px 10px rgba(0, 0, 0, 0.2)",borderRadius:"3px",border:"1px solid #ddd"
}}
>
{suggestions.map((sugg, i) => (
@ -150,7 +147,8 @@ const TagInput = ({
key={i}
className="dropdown-item p-1 hoverBox"
onClick={() => handleSuggestionClick(sugg)}
style={{ cursor: "pointer", fontSize: "0.875rem" }}
style={{cursor: "pointer", fontSize: "0.875rem"}}
>
{sugg.name}
</li>
@ -161,5 +159,4 @@ const TagInput = ({
</>
);
};
export default TagInput;