nextui-org / nextui

🚀 Beautiful, fast and modern React UI library.
https://nextui.org
MIT License
21.57k stars 1.41k forks source link

[BUG] - AUTOCOMPLETE_BUG_WITH_VALUE_ONINPUTCHANGE #3405

Open peppe180902 opened 2 months ago

peppe180902 commented 2 months ago

NextUI Version

THIS IS THE SANDBOX LINK: sandbox-link

2.4.2

Describe the bug

Hi, I have 3 autocompletes that work in condition with each other. Only if the first has been completed will the second be enabled and so on for the third, otherwise they will start with an isDisabled value.

The problem is that if I have completed all 3 fields, when I empty the second the third is automatically disabled because there is a condition that I inserted so if it is empty it cannot be enabled, at this point I put a another condition whereby if this is emptied the value of the third input must be reset in order to return everything to normal, however what happens is that visually the value that the user had entered remains but if I make a console.log I see that the true value it has been reset in this way all the input is buggy because the user sees value 4 instead it has been reset so it is 0.

I attach the code and video to show you: `import React, { useEffect, useRef, useState } from "react"; import { Autocomplete, AutocompleteItem } from "@nextui-org/react"; import { civic } from "visure/data/geographic";

interface AutoCompleteCivicProps { civicValue: number; setCivicValue: React.Dispatch<React.SetStateAction>; isDisabled?: boolean; }

/ ERROR: QUANDO IL L'ADDRESS VIENE SVUOTATO IL CIVICO SI SETTA A 0 MA NELL'INPUT COME VALORE VISIVO RIMANE IL PRECEDENTE IN REALTà IL VALORE E' 0 MA VISIVAMENTE NON VIENE AGGIORNATO => CAPIRE IL PERCHE' E RISOLVERE /

export const AutoCompleteCivic: React.FC = ({ civicValue, setCivicValue, isDisabled }) => { const [touched, setTouched] = useState(false); const [valid, setValid] = useState(false); const inputRef = useRef(null);

const isValidCivic = (value: number) => {
    return civic.some(civicItem => civicItem.value === value);
};

const onInputChange = (value: string) => {
    setCivicValue(Number(value))
    setValid(isValidCivic(Number(value)));
};

const onClose = () => {
    setTouched(true);
    setValid(isValidCivic(civicValue));
};

useEffect(() => {
    if (civicValue === 0 && inputRef.current) {
        inputRef.current.value = ''; // Clear the input visually
    }
}, [civicValue]);

console.log('civicValueInsideAutoComplete', civicValue)

return (
    <Autocomplete
        classNames={{
            base: 'w-full',
            listbox: 'w-full',
        }}
        label="Cerca il civico..."
        variant="bordered"
        defaultItems={civic}
        //key={civicValue}
        value={civicValue}
        isDisabled={isDisabled}
        onInputChange={onInputChange}
        onClose={onClose}
        color={valid ? 'success' : 'default'}
        listboxProps={{
            emptyContent: 'Nessun civico trovato',
        }}
    >
        {(item) => <AutocompleteItem key={item.value}>{item.label}</AutocompleteItem>}
    </Autocomplete>
);

}`

Your Example Website or App

No response

Steps to Reproduce the Bug or Issue

i have reported everitingh in my screen video

Expected behavior

Input clear when is disabled

Screenshots or Videos

https://github.com/nextui-org/nextui/assets/135150478/fee5d4ab-d2a7-4bf2-9700-60a15aada593

Operating System Version

macOs

Browser

Chrome

linear[bot] commented 2 months ago

ENG-1106 [BUG] - AUTOCOMPLETE_BUG_WITH_VALUE_ONINPUTCHANGE

wingkwong commented 2 months ago

please provide a minimal reproducible example (e.g stackblitz).

peppe180902 commented 2 months ago

please provide a minimal reproducible example (e.g stackblitz).

how to do it?

awesome-pro commented 2 months ago

@peppe180902 you can go to

https://codesandbox.io/p/devbox/funny-archimedes-ht3dtn?file=%2FApp.jsx&utm_medium=sandpack use sandbox It is just like a virtual VS code Put you necessary code (demo) here to show the bug and then share the link of that project showing bug.

I hope you find it helpful :)

peppe180902 commented 2 months ago

@peppe180902 you can go to

https://codesandbox.io/p/devbox/funny-archimedes-ht3dtn?file=%2FApp.jsx&utm_medium=sandpack use sandbox It is just like a virtual VS code Put you necessary code (demo) here to show the bug and then share the link of that project showing bug.

I hope you find it helpful :)

this is the sandbox please help me: https://codesandbox.io/p/devbox/ecstatic-yalow-hzcjt9

awesome-pro commented 2 months ago

@wingkwong I have reproduces the bug, as per the provided reference and I want to work on it. Should I continue ?

awesome-pro commented 2 months ago

@peppe180902 The actual issue with your code is that it uses value & onValueChange props which are not actual InputProps (see here) in this file https://github.com/nextui-org/nextui/blob/canary/packages/components/autocomplete/src/use-autocomplete.ts

Screenshot 2024-07-07 at 5 28 50 PM

So you can use selectedKey and onSelectionChange props for changing the value of Text/value in Autocomplete. Something like this

"use client";

import React, { useState } from "react";
import {
  Autocomplete,
  AutocompleteItem,
  Button,
  Input,
} from "@nextui-org/react";
import { Key } from "@react-types/shared";

import { animals } from "./data";

export default function App() {
  const [key, setKey] = useState<Key | null | undefined>(null);

  return (
    <div className="flex flex-col gap-10 p-3">
      <Autocomplete
        className="max-w-xs"
        defaultItems={animals}
        label="Favorite Animal, search for one"
        placeholder="Search an animal"
        selectedKey={key}
        onSelectionChange={(key) => setKey(key)}
      >
        {(animal) => (
          <AutocompleteItem key={animal.value}>{animal.label}</AutocompleteItem>
        )}
      </Autocomplete>

      <div className="flex flex-col gap-3">
        <h1>Selected key: {key}</h1>

        <Button onClick={() => setKey("")}>Clear Value</Button>
        <Button onClick={() => setKey("dog")}>Set Dog</Button>
        <Button onClick={() => setKey("cat")}>Set Cat</Button>
        <Button onClick={() => setKey("bird")}>Set Bird</Button>
      </div>

      <Input
        placeholder="Type a value"
        value={key?.toString()}
        onValueChange={(value) => setKey(value)}
      />
    </div>
  );
}

This will resolve your issue :)

@wingkwong is this workaround ok, OR I should work towards making the use of value Props possible for changing the value inside autocomplete ? So in that case, value prop will be brought in use removing it from Omit which need to handled carefully in case of multi-selections.

wingkwong commented 2 months ago

@abhinandan-verma

The actual issue with your code is that it uses value & onValueChange props

where does it use onValueChange in the sandbox code?

Also the issue (as shown in the video) is that the third autocomplete value got reset after cleaning the second one. Based on your statement, it doesn't explain anything and I don't know why Input component is here while the demo only contains 3 autocomplete components. You should fork the sandbox and modify on top of it to prove the proposed change s working.

awesome-pro commented 2 months ago

Hey @peppe180902 I have debugged your issue now. I understand the delay but I was busy in some other works

I have simplified your logic in a single component, and it works completely fine the way you want it to work :) Actually keys in autocomplete are controlled by selectedKey and onSelectionChange and not the value prop. The following code implements the same logic and changes the state of consecutive Autocompletes in desired way

"use client";

import React, {  useEffect, useState } from "react";
import { Autocomplete, AutocompleteItem, Input } from "@nextui-org/react";

import { animalsData } from "./data";

type Key = string | number;

const TestTemplate = () => {
  const [addressKey, setAddressKey] = useState<Key>("");
  const [key, setKey] = useState<Key>("");
  const [civicKey, setCivicKey] = useState<Key>("");

  const [isVisibileAddress, setIsVisibleAddress] = useState<boolean>(false);
  const [isVisibleCivic, setIsVisibleCivic] = useState<boolean>(false);

  const checkCityKey = (value: Key) => {
    return animalsData.some((cityItem) => cityItem.value === value);
  };

  const checkCAddressKey = (value: Key) => {
    return animalsData.some((cityItem) => cityItem.value === value);
  }

  useEffect(() => {
    if (checkCityKey(key)) {
      setIsVisibleAddress(true);
    } else {
      setIsVisibleAddress(false);
      setAddressKey("");
    }

    if(checkCAddressKey(addressKey)) {
      setIsVisibleCivic(true);
    } else {
      setIsVisibleCivic(false);
      setCivicKey("");
    }
  }, [key, addressKey]);

  return (
    <div className="flex flex-col gap-4 w-[500px]">
      <Autocomplete
        className="max-w-xs"
        defaultItems={animalsData}
        label="Favorite Animal, search for one"
        placeholder="Search an animal"
        selectedKey={key}
        onSelectionChange={(key) => setKey(key as Key)}
      >
        {(animal) => (
          <AutocompleteItem key={animal.value}>{animal.label}</AutocompleteItem>
        )}
      </Autocomplete>

      <Autocomplete
        className="max-w-xs"
        defaultItems={animalsData}
        isDisabled={!isVisibileAddress}
        label="Favorite Animal, search for one"
        placeholder="Search an animal"
        selectedKey={addressKey}
        onSelectionChange={(key) => setAddressKey(key as Key)}
      >
        {(animal) => (
          <AutocompleteItem key={animal.value}>{animal.label}</AutocompleteItem>
        )}
      </Autocomplete>

      <Autocomplete
        className="max-w-xs"
        defaultItems={animalsData}
        isDisabled={!isVisibleCivic}
        label="Favorite Animal, search for one"
        placeholder="Search an animal"
        selectedKey={civicKey}
        onSelectionChange={(key) => setCivicKey(key as Key)}
      >
        {(animal) => (
          <AutocompleteItem key={animal.value}>{animal.label}</AutocompleteItem>
        )}
      </Autocomplete>

      <div className="flex flex-col gap-3 bg-secondary-200/40 p-4">
        <h4>Selected City key: {key}</h4>
        <h4>Selected Address key: {addressKey}</h4>
        <h4>Selected Civic key: {civicKey}</h4>
      </div>
      <div className="flex flex-col gap-3 bg-success-200/40 p-4">
        <h3>Is visible address: {isVisibileAddress.toString()}</h3>
        <h3>Is visible civic: {isVisibleCivic.toString()}</h3>
      </div>
    </div>
  );
};

export default TestTemplate;

data.ts

export type Animal = {
  label: string;
  value: string;
  description?: string;
};

export const animalsData: Animal[] = [
  {
    label: "Cat",
    value: "cat",
    description: "The second most popular pet in the world",
  },
  {
    label: "Dog",
    value: "dog",
    description: "The most popular pet in the world",
  },
  {
    label: "Elephant",
    value: "elephant",
    description: "The largest land animal",
  },
  { label: "Lion", value: "lion", description: "The king of the jungle" },
  { label: "Tiger", value: "tiger", description: "The largest cat species" },
  {
    label: "Giraffe",
    value: "giraffe",
    description: "The tallest land animal",
  },
  {
    label: "Dolphin",
    value: "dolphin",
    description: "A widely distributed and diverse group of aquatic mammals",
  },
  {
    label: "Penguin",
    value: "penguin",
    description: "A group of aquatic flightless birds",
  },
  {
    label: "Zebra",
    value: "zebra",
    description: "A several species of African equids",
  },
  {
    label: "Shark",
    value: "shark",
    description:
      "A group of elasmobranch fish characterized by a cartilaginous skeleton",
  },
  {
    label: "Whale",
    value: "whale",
    description: "Diverse group of fully aquatic placental marine mammals",
  },
  {
    label: "Otter",
    value: "otter",
    description: "A carnivorous mammal in the subfamily Lutrinae",
  },
  {
    label: "Crocodile",
    value: "crocodile",
    description: "A large semiaquatic reptile",
  },
];

https://github.com/user-attachments/assets/f3b52aaf-29f4-45af-8acf-06a8a49e4293

I hope your problem will be solved now, feel free to ask if still any issue occurs