Maximum Update Depth Exceeded on a search modal

I have a dynamic search modal that queries the backend as users type. When a user selects one of the options it saves that option in state and is rendered as a Chip component. However, after updating our dependencies, the modal will not launch and the console is displaying this message

“Warning: Maximum update depth exceeded. This can happen when a
component calls setState inside useEffect, but useEffect either
doesn’t have a dependency array, or one of the dependencies changes on
every render.”

I logged the dependencies of my useEffect and none of them appear to be changing from render to render, however they are logged more times than I’d expect. Here is the component:

const SearchModalReturnField = ({
  open,
  fetchService,
  fetchQueryFieldName,
  handleClose,
  value,
  setValue,
  ...props
}) => {
  const [modalValue, setModalValue] = useState([]);
  const [inputValue, setInputValue] = useState('');
  const [options, setOptions] = useState([]);
  const [error, setError] = useState();

  const handleChangeInput = (event) => {
    setInputValue(event.target.value);
  };

  const handleChange = (event, newValue) => {
    setModalValue(uniqWith(newValue, isEqual));
  };

  const handleSelectIngredient = (event, newValue) => {
    setInputValue('');
    if (options.length > 0) {
      setModalValue(uniqWith([...modalValue, newValue], isEqual));
    }
  };

  const fetch = useMemo(
    () =>
      debounce((params, callback) => {
        fetchService(params)
          .then(callback)
          .catch((error) => {
            console.error(error);
            if (error && error.message) {
              return setError('Error: ' + error.message);
            }
            if (typeof error === 'string') {
              return setError('Error: ' + error);
            }
            return setError('Error encountered');
          });
      }, 500),
    [fetchService],
  );

  useEffect(() => {
    let active = true;

    if (inputValue === '') {
      setOptions([]);
      return undefined;
    }

    if (fetchService) {
      const params = {
        query: inputValue,
        limit: 200,
      };
      fetch(params, (results) => {
        if (active) {
          setOptions(map(results, fetchQueryFieldName) || []);
        }
      });
    }

    return () => {
      active = false;
    };
  }, [inputValue, fetchQueryFieldName]);

  const handleDelete = (item) => {
    setModalValue(filter(modalValue, (v) => v !== item));
  };

  const handleAccept = () => {
    setValue(uniqWith(concat(value, modalValue), isEqual));
    setModalValue([]);
    handleClose();
  };

  const handleCloseModal = () => {
    setModalValue([]);
    handleClose();
  };

  const optionsList = options.map((option, index) => {
    return (
      <OptionLabel
        key={index}
        onClick={(e) => handleSelectIngredient(e, option)}
        data-test="searchModal-foodOption"
      >
        {getHighlightedText(capitalize(option), inputValue)}
      </OptionLabel>
    );
  });

  if (isMobile()) {
    // Mobile view
    return (
      <MobileDialog
        open={open}
        handleClose={handleClose}
        height="fit-content"
        maxheight="85%"
        small="small"
        data-test="mobile-modal"
      >
        <DialogContainer flexDirection="column" alignItems="center">
          <Puller />
          <TopContainer flexDirection="column" alignItems="center">
            <MobileHeading>{props.heading}</MobileHeading>
            <MobileSubheading marginTop={'8px'}>
              {props.subheading}
            </MobileSubheading>
          </TopContainer>
          <SearchContainer flexDirection="column" alignSelf="flex-start">
            <MobileInputLabel>Search for food</MobileInputLabel>
            <DynamicSearchBar
              searchTerm={inputValue}
              handleChange={handleChangeInput}
              data-test="searchModal-ingSearch"
            />
          </SearchContainer>
          <MobileChipContainer wrap="wrap" alignItems="center">
            {modalValue.map((option, index) => (
              <ChipStyled
                key={index}
                label={props.getOptionLabel(option)}
                handleDelete={() => handleDelete(option)}
                bgcolor={props.chipBgColor}
                iconcolor="#FFFBFB"
                selected
                data-test="searchModal-ingChip"
              />
            ))}
          </MobileChipContainer>
          <OptionsContainer flexDirection="column">
            {optionsList}
          </OptionsContainer>
          {error && (
            <AlertStack
              messages={error}
              type="error"
              variant="filled"
              open={!!error}
              handleClose={() => setError(null)}
              autoHideDuration={10000}
            />
          )}

          <MobileAddButton
            alignItems="center"
            justify="center"
            backgroundcolor={props.chipBgColor}
            onClick={handleAccept}
            data-test="submit-items"
          >
            <MobileSubheading color="#fff">
              Add {props.heading}
            </MobileSubheading>
          </MobileAddButton>
          <div style={{ height: 8 }} />
          <MobileAddButton
            alignItems="center"
            justify="center"
            backgroundcolor="#fff"
            borderColor={colors.primary800}
            onClick={handleCloseModal}
          >
            <MobileSubheading color={colors.primary800}>
              Cancel
            </MobileSubheading>
          </MobileAddButton>
        </DialogContainer>
      </MobileDialog>
    );
  } else {
    // Web View
    return (
      <Dialog onClose={handleClose} open={open}>
        <DialogTitle>
          {handleClose ? (
            <TitleButton aria-label="close" onClick={handleCloseModal}>
              <CloseIcon />
            </TitleButton>
          ) : null}
        </DialogTitle>
        <DialogContent>
          <Title>Search Foods</Title>
          <AutocompleteStyled
            multiple
            autoComplete
            freeSolo
            includeInputInList
            filterOptions={(x) => x}
            options={options}
            getOptionLabel={props.getOptionLabel || ((option) => option.name)}
            value={modalValue}
            onChange={handleChange}
            renderInput={(params) => (
              <InputBase
                ref={params.InputProps.ref}
                inputProps={{
                  ...params.inputProps,
                  onChange(event) {
                    handleChangeInput(event);
                    return params.inputProps.onChange(event);
                  },
                }}
                placeholder={props.placeholder || ''}
                fullWidth
                endAdornment={
                  <InputAdornment position="end">
                    <IconButton aria-label="search" edge="end">
                      <SearchIcon />
                    </IconButton>
                  </InputAdornment>
                }
                data-test="searchModal-ingSearch"
              />
            )}
          />
          {error && (
            <AlertStack
              messages={error}
              type="error"
              variant="filled"
              open={!!error}
              handleClose={() => setError(null)}
              autoHideDuration={10000}
            />
          )}
          {modalValue.map((option, index) => (
            <ChipStyled
              key={index}
              label={props.getOptionLabel(option)}
              handleDelete={() => handleDelete(option)}
              bgcolor={props.chipBgColor}
              hovercolor={props.hoverColor}
              iconcolor="#FFFBFB"
              data-test="searchModal-ingChip"
              selected
            />
          ))}
        </DialogContent>
        <DialogActions>
          <AddButton
            buttonText="ADD"
            onClick={handleAccept}
            backgroundcolor={props.chipBgColor}
            hovercolor={props.hoverColor}
            data-test="searchModal-add"
          />
        </DialogActions>
      </Dialog>
    );
  }
};