Amin Soraya
Amin Soraya

Reputation: 89

NextJS: Maximum update depth exceeded error When using custom Component

I am creating a reusable multiselect combobox component using shadcn and typescript and everything is properly in simple place (directly using in page) . but I get an error when using in complicated form (using inside a dialog or other place) :

Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.

NextJs code:

"use client";
import * as React from "react";
import { ChevronDown, CircleX, Search, X } from "lucide-react";
import { cn, GetColor } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover";
import { ComboBoxItems, TComboBox } from "@/types";
import { Checkbox } from "./checkbox";
import { FC } from "react";

export default function Combobox(props: TComboBox) {
  const {
    items,
    placeholder,
    searchable,
    multiSelect,
    values,
    onChange,
    showError,
  } = props;
  const [open, setOpen] = React.useState(false);
  const [serachKeyword, setSearchKeyword] = React.useState<
    string | undefined
  >();
  const[data,setData]=React.useState<string[]|undefined>()

  const filteredItems = items.filter(
    (item) => item.label!.includes(serachKeyword || "") ?? item
  );
  const getSelectedColors = items
    .filter((s) => values?.includes(s.value!))
    .map((s) => ({ color: s.color, value: s.value }));

  const handleChanges = (value: string) => {
    const findedItem = values?.find((s) => s == value);

    if (!multiSelect && values?.length == 1 && !findedItem) {
      setOpen(false);
      return;
    }

    if (findedItem) {
      const allExceptValue = values.filter((s) => s != value);
      onChange(allExceptValue);
    } else {
      const fixValues = values ? [...values, value] : [value];
      onChange(fixValues);
    }
  };

  return (
    <React.Fragment>
      <Popover open={open} onOpenChange={setOpen}>
        <PopoverTrigger asChild className={`ComboBoxTrigger`}>
          <Button
            variant="white"
            role="combobox"
            aria-expanded={open}
            className={cn(
              "w-full justify-between !h-[67px] hover:bg-white",
              showError?"!border-red-500":null
            )}
          >
            {placeholder}
            <ChevronDown
              strokeWidth={1.6}
              className={cn("transition-all", open && "rotate-180")}
            />
          </Button>
        </PopoverTrigger>
        <PopoverContent align="start" className="ComboboxContent">
          {searchable && <ComboboxSearch onType={setSearchKeyword} />}
          {filteredItems.map((item) => (
            <ComboxItem
              key={item.value}
              checked={values?.includes(item.value!)}
              {...item}
              callback={handleChanges}
            />
          ))}
        </PopoverContent>
      </Popover>
      <Colors values={getSelectedColors}  onClose={handleChanges} />
    </React.Fragment>
  );
}

const Colors: FC<{
  values: Array<{ color: string | undefined; value: string | undefined }>;
  onClose: (val: string) => void;
}> = ({ values, onClose }) => {
  return (
    values != undefined && (
      <div className="mt-5 flex  gap-3 py-2">
        {values?.map(({ color, value }, index) => (
          <span
            key={index}
            style={{ background: GetColor(color!) }}
            className="min-w-8 min-h-8 rounded-sm relative"
          >
            <X
              onClick={() => onClose(value!)}
              className="absolute -top-2 -left-2  text-xs bg-red-500 rounded-full text-white cursor-pointer p-1 h-5 w-5"
            />
          </span>
        ))}
      </div>
    )
  );
};

const ComboxItem: React.FC<
  Partial<ComboBoxItems> & {
    callback: (val: string) => void;
    checked?: boolean;
  }
> = (props) => {
  const { checkbox, label, color, value, callback, checked } = props;

  return (
    <div className="flex items-center gap-2 " onClick={() => callback(value!)}>
      {checkbox && <Checkbox checked={checked} />}
      {color && (
        <span
          style={{ background: GetColor(color!) }}
          className="w-8 h-8 rounded-sm"
        ></span>
      )}
      <span className="whitespace-nowrap  text-lg">{label}</span>
    </div>
  );
};

const ComboboxSearch: React.FC<{ onType: (keyword: string) => void }> = ({
  onType,
}) => {
  return (
    <div className="flex items-center gap-2 relative">
      <Search className="size-6 text-gray-500" />
      <input
        onChange={({ target }) => onType(target.value)}
        type="text"
        className="w-full py-2 px-3  rounded outline-none"
        placeholder="جستجو..."
      />
    </div>
  );
};

This is how I use it.:

 const {
    errors,
    handleSubmit,
    handleBlur,
    handleChange,
    touched,
    setFieldValue,
    values,
    setFieldTouched,
  } = useFormik<ICreateEditProductForm>({
    initialValues: {
 
      colors: [],
    },
    validationSchema: createEditProductSchema,
    onSubmit: (values) => {
      console.log(values);
    },
  });

              <Combobox
                items={colors}
                onChange={(selectedColors) => {
                  setFieldValue("colors", selectedColors);
                }}
                placeholder="انتخاب رنگ"
                values={values.colors}
                multiSelect={true}
                searchable
                showError={touched.colors && errors.colors !== undefined}
              />

So how can I prevent error?

Upvotes: -1

Views: 36

Answers (0)

Related Questions