【问题标题】:Material UI Autocomplete- Popper not STUCk to the component which rendered itMaterial UI Autocomplete-Popper 不会停留在渲染它的组件上
【发布时间】:2025-12-14 21:50:02
【问题描述】:

我的自动完成的当前输出在附加的视频链接中,当窗口滚动时,自动完成建议在页面周围跳舞:https://www.screencast.com/t/atbTdIaXpzu

我正在尝试将自动完成建议修复到自动完成控件,并且自动建议下拉菜单以某种方式粘在自动完成上,但自动完成中的滚动体验并不那么流畅

解决方案如下:

使用自动完成属性 PopperComponent ={PopperMy}

const PopperMy = function (props) {
  return <Popper {...props} placement="bottom-start" disablePortal={true} />;
};

或者这个 popperVariant :

<Popper
      {...props}
      disablePortal={true}
      placement="bottom-start"
      popperOptions={{ positionFixed: true }}
    />

感谢任何建议和修复。谢谢!

在此处添加文件内容以供参考:

import React, { useState, useEffect } from "react";
import {
  TextInputField,
  InputLabelField,
  BoxField,
  CustomWhiteTooltip,
} from "../../atoms";
import Autocomplete, {
  createFilterOptions,
} from "@material-ui/lab/Autocomplete";
import {
  IsArrayNotEmpty,
  IsNotEmpty,
  IsString,
  IsObject,
  IsFunction,
  ThrowException,
  IsObjectNotEmpty,
  ObjectHasKey,
  IsNotUndefined,
} from "../../../utility";
import UserMessages from "../../../constants/UserMessages";
import TextField from "@material-ui/core/TextField";
import { InfoIcon } from "../../../resources/ThemeIcons";
import { Popper } from "@material-ui/core";


const TypeaheadField = ({
  id = "",
  renderInputId = "",
  listOptions,
  mainListOptionsKey = IsString(mainListOptionsKey)
    ? mainListOptionsKey
    : "title",
  getPropertyFromSingleObject = IsString(getPropertyFromSingleObject)
    ? getPropertyFromSingleObject
    : false,
  //defaultSelected='',
  isCreatable = false,
  onChange,
  addNewLabel = "Add",
  disableClearable = false,
  className = "",
  textFieldClassName = "",
  isSelectMultiple = false,
  limitTags = -1,
  filterSelectedOptions = true,
  openOnFocus = true,
  blurOnSelect = false,
  labelAtTop = false,
  labelClassName = "",
  popupIcon,
  size = "small",
  disabled = false,
  variant = "standard",
  label = "",
  required = false,
  placeholder = "",
  fullWidth = true,
  value = "",
  ChipProps = {},
  inputLabelProps = {},
  helperText = "",
  error = false,
  onBlur = "",
  name = "",
  tooltipMessage = "",
  tooltipIcon = "",
  ...rest
}) => {
  /* Required props:  listOptions, onChange */
  const isListOptionsProvided = IsArrayNotEmpty(listOptions);
  const isOnChangeProvided = IsFunction(onChange);
  if (!isListOptionsProvided || !isOnChangeProvided) {
    //ThrowException(UserMessages.warnings.missingRequiredProps);
  }

  const [autovalue, setValue] = React.useState(value);

  useEffect(() => {
    const IsMultipleSelectAllowed = isSelectMultiple === true;
    if (IsMultipleSelectAllowed) {
      setValue(autovalue && IsArrayNotEmpty(autovalue) ? autovalue : []);
    } else if (!IsMultipleSelectAllowed) {
      setValue(autovalue && IsNotEmpty(autovalue) ? autovalue : null);
    }
  }, []);

  const handleOnChange = (event, newValue) => {
    let selectedValue = newValue;

    if (newValue && IsObject(newValue)) {
      if (newValue[mainListOptionsKey]) {
        // Create a new value from the user input
        if (newValue.type === "new") {
          if (IsString(listOptions[0])) {
            selectedValue = newValue.value;
            listOptions.push(newValue.value);
          } else {
            selectedValue = newValue.value;
            listOptions.push({
              [mainListOptionsKey]: newValue.value,
            });
          }
        }
      } else {
        const LastObject = IsArrayNotEmpty(newValue)
          ? newValue[newValue.length - 1]
          : null;
        const IsNewValueAdded =
          LastObject && LastObject.type === "new" && LastObject.value;
        if (IsNewValueAdded) {
          // Create a new value from the user input
          const CurrentValues =
            autovalue && IsArrayNotEmpty(autovalue) ? [...autovalue] : [];
          if (IsString(listOptions[0])) {
            selectedValue = [...CurrentValues, LastObject.value];
            listOptions.push(LastObject.value);
          } else {
            selectedValue = [
              ...CurrentValues,
              { [mainListOptionsKey]: LastObject.value },
            ];
            listOptions.push({ [mainListOptionsKey]: LastObject.value });
          }
        }
      }
    }

    setValue(selectedValue);
    if (
      getPropertyFromSingleObject !== false &&
      isSelectMultiple === false &&
      IsObjectNotEmpty(selectedValue) &&
      ObjectHasKey(selectedValue, getPropertyFromSingleObject)
    ) {
      onChange(selectedValue[getPropertyFromSingleObject]);
      return;
    }
    onChange(selectedValue);
  };

  const handleFilterOptions = (options, params) => {
    const filter = createFilterOptions();
    const filtered = filter(options, params);

    // Suggest the creation of a new value
    const IsSuggestNewValue = params.inputValue !== "" && isCreatable === true;
    if (IsSuggestNewValue) {
      filtered.push({
        value: params.inputValue,
        type: "new",
        [mainListOptionsKey]: `${addNewLabel} "${params.inputValue}"`,
      });
    }
    return filtered;
  };

  const handleGetOptionLabel = (option) => {
    if (typeof option === "string") {
      return option;
    }
    return option ? option[mainListOptionsKey] : null;
  };

  const handleGetOptionSelected = (option, value) => {
    if (value && IsObject(value) && IsObjectNotEmpty(value)) {
      return option[mainListOptionsKey] === value[mainListOptionsKey];
    } else if (
      option &&
      IsObject(option) &&
      IsObjectNotEmpty(option) &&
      value &&
      IsString(value) &&
      IsNotEmpty(value)
    ) {
      return option[mainListOptionsKey] === value;
    } else {
      return option === value;
    }
  };

  const handleEmptyValue = (valueParam) => {
    /**
     * TODO:: Add these in utility folder.
     */
    if (isSelectMultiple) {
      return value && IsArrayNotEmpty(value)
        ? value
        : valueParam && IsArrayNotEmpty(valueParam)
        ? valueParam
        : [];
    } else {
      if (IsObject(valueParam) || IsObject(value)) {
        return IsNotUndefined(value) &&
          value !== null &&
          IsObjectNotEmpty(value)
          ? value
          : IsNotUndefined(valueParam) &&
            valueParam !== null &&
            IsObjectNotEmpty(valueParam)
          ? valueParam
          : null;
      } else if (IsString(valueParam) || IsString(value)) {
        return IsNotEmpty(value)
          ? value
          : IsNotEmpty(valueParam)
          ? valueParam
          : null;
      }
    }
  };

  const handleOnBlur = () => {
    if (IsFunction(onBlur)) {
      onBlur();
    }
  };

  const autocompleteValue = handleEmptyValue(autovalue);

  const inputRequired =
    required === true &&
    (isSelectMultiple
      ? !IsArrayNotEmpty(autocompleteValue)
      : !IsNotEmpty(autocompleteValue));

  return (
    <Autocomplete
      className={className}
      multiple={isSelectMultiple}
      value={autocompleteValue}
      filterSelectedOptions={filterSelectedOptions}
      id={id}
      size={size}
      ChipProps={ChipProps}
      disableClearable={disableClearable}
      popupIcon={popupIcon}
      openOnFocus={openOnFocus}
      blurOnSelect={blurOnSelect}
      options={listOptions}
      limitTags={limitTags}
      onChange={handleOnChange}
      disabled={disabled}
      filterOptions={handleFilterOptions}
      getOptionLabel={handleGetOptionLabel}
      getOptionSelected={handleGetOptionSelected}
      
      name={name}
      onBlur={handleOnBlur}
      renderInput={(params) => (
        <div>
       <TextField
            {...params}
            id={renderInputId ? renderInputId : label}
            variant={variant}
            label={!labelAtTop && label}
            className={textFieldClassName}
            required={inputRequired}
            //onChange={()=>{}}
            placeholder={placeholder}
            fullWidth={fullWidth}
            InputLabelProps={inputLabelProps}
            helperText={helperText}
            error={error}           
          /> 
        </div>
      )}
    />
  );
};

export default TypeaheadField;

【问题讨论】:

    标签: javascript reactjs autocomplete material-ui


    【解决方案1】:

    我想了解更多关于您的代码的信息,以便获得更好的上下文。第一个建议是使用道具 anchorEl 并引用您的 Textfield。您可以在此处的文档中找到有关它的更多信息和示例:

    https://material-ui.com/es/components/popper/

    而且我不知道你是否直接使用自动完成组件,如果你是,请告诉我。

    【讨论】:

    • 感谢您的时间@LittleOrange。基本上,我写了一个通用自动完成,它消耗了自动完成变体(分组,限制标签)可以拥有的所有可能的道具。我在我的应用程序中引用了这个。在我上面的主要问题中添加了 AutComplete 的整个源代码。
    • 我分享的 Popper 代码只是为了修复自动完成的选项,否则我实际上不需要它。我宁愿让自动完成功能在 UI 中自行执行默认选项。但遗憾的是,这里并非如此。