Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

useState doesnt set state after API call is getting made during second time: MUI+ReactJS

I am using the Material-UI Autocomplete component for a search box but am running into an issue. The options are fetched from an API and passed to the component. The first time I open the dropdown, the list of options appears as expected. However, when I close and open the dropdown again, it shows no options and the loading icon appears continuously. My desired functionality is that the user can fetch the options at the beginning, then when typing, the options are refetched with a loading icon present. When there are no results, it should return "No options found".

Sandbox: https://codesandbox.io/s/autocomplete-with-typescript-forked-3l2777?file=/src/App.tsx

import * as React from "react";
import { Autocomplete, TextField } from "@mui/material";
import SearchIcon from "@material-ui/icons/Search";
import InputAdornment from "@material-ui/core/InputAdornment";
import CircularProgress from "@material-ui/core/CircularProgress";

interface Option {
  label: string;
  value: number;
}

interface IProps {
  fetchServerSideOptions?: (inputValue: string) => void;
  loading?: boolean;
  open?: boolean;
  onOpen?: (event: React.SyntheticEvent) => void;
  onClose?: (event: React.SyntheticEvent, reason: string) => void;
  options: Option[];
  selected: Option;
  setSelected: React.Dispatch<React.SetStateAction<Option>>;
}

export default function Auto(props: IProps) {
  const {
    options = [],
    selected = [],
    setSelected,
    open,
    loading,
    onOpen,
    onClose,
    fetchServerSideOptions
  } = props;
  return (
    <Autocomplete
      forcePopupIcon={false}
      options={options}
      value={selected}
      open={open}
      loading={loading}
      onOpen={onOpen}
      onClose={onClose}
      onChange={(_event, value) => setSelected(value)}
      onInputChange={(_event, value) => fetchServerSideOptions?.(value)}
      renderInput={(params) => {
        return (
          <TextField
            {...params}
            placeholder="Select"
            InputProps={{
              ...params.InputProps,
              startAdornment: (
                <React.Fragment>
                  <InputAdornment position="start">
                    <SearchIcon />
                  </InputAdornment>
                  {params.InputProps.startAdornment}
                </React.Fragment>
              ),
              endAdornment: (
                <React.Fragment>
                  {loading ? (
                    <CircularProgress color="inherit" size={20} />
                  ) : null}
                  {params.InputProps.endAdornment}
                </React.Fragment>
              )
            }}
          />
        );
      }}
    />
  );
}
import { useState, useEffect } from "react";
import Auto from "./Auto";

interface Option {
  label: string;
  value: number;
}

export default function App() {
  const [options, setOptions] = useState<Option[]>([]);
  const [selected, setSelected] = useState<Option | null>(null);
  const [open, setOpen] = useState<boolean>(false);
  const loading = open && options.length === 0;

  const fetchOptions = async (value: string) => {
    const response: Response = await fetch(
      `https://dummyjson.com/products/search?q=${value}&limit=10`
    );
    const { products } = await response.json();
    const formattedOptions = products.map(({ title, id }) => {
      return { label: title, value: id };
    });
    console.log(formattedOptions);
    setOptions(formattedOptions);
  };

  useEffect(() => {
    fetchOptions("");
  }, []);

  useEffect(() => {
    if (!open) {
      setOptions([]);
    }
  }, [open]);

  return (
    <Auto
      selected={selected}
      setSelected={setSelected}
      options={options}
      open={open}
      loading={loading}
      onOpen={() => setOpen(true)}
      onClose={() => setOpen(false)}
      fetchServerSideOptions={(value) => fetchOptions(value)}
    />
  );
}

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

You can remove second useEffect that is not required.

  useEffect(() => {
    if (!open) {
      setOptions([]);
    }
  }, [open]);

Add a state which shows when it is loading(when API call is going on) not when length is 0

  const [loading, setLoading] = useState(false);
  // const loading = open && options.length === 0;

LIVE DEMO

export default function App() {
  const [options, setOptions] = useState<Option[]>([]);
  const [selected, setSelected] = useState<Option | null>(null);
  const [open, setOpen] = useState<boolean>(false);
  const [loading, setLoading] = useState(false);
  // const loading = open && options.length === 0;

  const fetchOptions = async (value: string) => {
    try {
      setLoading(true); // CHANGE
      const response: Response = await fetch(
        `https://dummyjson.com/products/search?q=${value}&limit=10`
      );
      const { products } = await response.json();
      const formattedOptions = products.map(({ title, id }) => {
        return { label: title, value: id };
      });
      console.log(formattedOptions);
      setOptions(formattedOptions);
    } catch (error) {
      console.error(error);
    } finally {
      setLoading(false); // CHANGE
    }
  };

  useEffect(() => {
    fetchOptions("");
  }, []);

  // useEffect(() => {
  //   if (!open) {
  //     setOptions([]);
  //   }
  // }, [open]);

  return (
    <Auto
      selected={selected}
      setSelected={setSelected}
      options={options}
      open={open}
      loading={loading}
      onOpen={() => setOpen(true)}
      onClose={() => setOpen(false)}
      fetchServerSideOptions={(value) => fetchOptions(value)}
    />
  );
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading