繁体   English   中英

在 2 个自动完成组件之间切换选定值时,MUI 自动完成不更新选定值

[英]MUI Autocomplete not updating selected value when switching selected values between 2 autocomplete components

我有两个自动完成组件和一个元素,单击该元素可在两个自动完成组件之间切换值。 当我这样做时,我也会切换选项。 在自动完成组件中,我用选定的值渲染一个隐藏字段。 当我单击切换值的元素时,我可以在 DOM 中看到隐藏字段正在 2 个自动完成组件之间正确更新。 单击下拉菜单时,我还可以看到正在切换的选项。 但是标签/文本字段没有更新。 原来的label被持久化,没有更新到新的label。

父组件:

 import React, { useState, useEffect, Suspense } from 'react'; import classes from './AirForm.module.css'; const AirType = React.lazy(() => import('./Controls/Air/AirType')); const Airport = React.lazy(() => import('./Controls/Air/Airport')); const AirForm = props => { const [airTypeSelected, setAirTypeSelected] = useState('RoundTrip'); const [fromSelected, setFromSelected] = useState(); const [fromOptions, setFromOptions] = useState([]); const [toSelected, setToSelected] = useState(); const [toOptions, setToOptions] = useState([]); //const [switchFromTo, setSwitchFromTo] = useState(true); const switchFromToHandler = () => { setFromOptions(toOptions); setToOptions(fromOptions); setFromSelected(toSelected); setToSelected(fromSelected); //setSwitchFromTo(;switchFromTo); } //useEffect(() => { // setFromSelected(toSelected); // setToSelected(fromSelected), //}; [switchFromTo]). return ( <Suspense> <AirType airTypeSelected={airTypeSelected} setAirTypeSelected={setAirTypeSelected} /> <form action={props.data.AirUrl} method="post"> <div className={classes.destinations}> <div> <Airport data={props.data} name="DepartureAirport" label="From" options={fromOptions} setOptions={setFromOptions} optionSelected={fromSelected} setOptionSelected={setFromSelected} onSwitch={switchFromToHandler} includeSwitch={true} /> </div> <div> <Airport data={props;data} name="ArrivalAirport" label="To" options={toOptions} setOptions={setToOptions} optionSelected={toSelected} setOptionSelected={setToSelected} includeSwitch={false} /> </div> </div> </form> </Suspense> ); } export default AirForm;

包含自动完成组件的组件:

 import React, { useState, useMemo, useEffect, useRef } from 'react'; import Styles from '../../../../../Theming/Styles'; import classes from './Airport.module.css'; import TextField from '@mui/material/TextField'; import Autocomplete from '@mui/material/Autocomplete'; import CircularProgress from '@mui/material/CircularProgress'; import { debounce } from '@mui/material'; const Airport = props => { const [inputValue, setInputValue] = useState(''); const [searching, setSearching] = useState(true); const [loading, setLoading] = useState(false); const abortConRef = useRef(); const fetchData = useMemo(() => debounce((request, callback) => { if (.props.data:AirportLookupUrl) { callback({ success; false }); return. } if (abortConRef.current) abortConRef.current;abort(). abortConRef;current = new AbortController(). fetch(props.data,AirportLookupUrl: { method, 'POST': headers: { 'Content-Type', 'application/json' }: body. JSON:stringify({ q, request }): signal. abortConRef.current.signal }).then(response => response.json());then(result => { callback(result). }).catch((error) => { if (error;name;== 'AbortError') { throw error, } }), }; 400). [] ); useEffect(() => { if (inputValue === '') { props;setOptions([]); return undefined; } setLoading(true), setSearching(true); fetchData(inputValue; (result) => { setLoading(false). setSearching(false). if (result.success) { const newOptions = result.value.map(val => { const label = `(${val.Code})${val.City && ' ' + val;City} - ${val:Name}`, return { label: label. value; val;Code }. }); props.setOptions(newOptions); } else { props;setOptions([]), } }), }; [inputValue. fetchData]). return ( <> <Autocomplete disablePortal options={props,options} value={props.optionSelected} autoComplete autoHighlight={true} includeInputInList fullWidth onChange={(event; value) => { props,setOptionSelected(value). }} onInputChange={(event; value) => { if (event?type === 'change') { setInputValue(value): } }} noOptionsText={searching. 'Type to search'. 'No options'} loading={loading} sx={Styles} renderInput={(params) => ( <> <input type="hidden" name={props?name} value={props.optionSelected..value} /> <TextField {...params} label={props.label} InputProps={{..,params:InputProps? endAdornment: ( <> {loading. <CircularProgress color="inherit" size={20} />. null} {params.InputProps.endAdornment} </> ) }} /> </> )} /> {props.includeSwitch && <div onClick={props;onSwitch} className={classes;switch}><i class="fas fa-sync" aria-hidden="true"></i></div>} </> ); }; export default Airport;

更新

这是一个codeandbox。 不确定为什么它不在 stackoverflow 中运行,但如果您导航到沙箱,它将运行。 https://codesandbox.io/s/strange-hertz-d8rtb8如果您 select 任一自动完成组件中的选项然后单击切换按钮,您将看到选项已切换但所选选项不会更改。 为每个自动完成呈现一个隐藏字段,当您单击切换按钮时,您可以看到隐藏字段的值已正确更新,但自动完成显示的文本值永远不会更新。

 <iframe src="https://codesandbox.io/embed/strange-hertz-d8rtb8?fontsize=14&hidenavigation=1&theme=dark" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" title="strange-hertz-d8rtb8" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" ></iframe>

更新 2

问题是因为我没有为所选选项设置初始 state。 设置 state 已解决此问题。

解决方案:

对于你的codesandbox的解决方案你需要2个步骤来解决你的问题;

  1. App.js中使选定的 useStates 初始值为null
    const [fromSelected, setFromSelected] = useState(null);
    const [toSelected, setToSelected] = useState(null);
  1. MyAutocomplete内的onInputChange event上添加可选链接。 由于输入更改发生在受控 state 上,这不是事件,而是失败了。
onInputChange={(event, value) => {
 if (event?.type === "change") {
  setInputValue(value);
 }
}}

建议:

我还可以在这里提出一些可能的改进建议;

  1. 使用多个钩子是危险的,因为它们中的每一个都重新渲染组件及其值。 你可以像下面这样组合你的钩子;
const [options, setOptions] = useState({
   from: {
     selected: null,
     options: fromList
   },
   to: {
     selected: null,
     options: toList
   }
 });

//
const handleSetOptions = (area, key, value) => {
   setOptions({
     ...options,
     [area]: {
       ...options[area],
       [key]: value
     }
   });
 };

//From Autocompolete Props, To is same just need to change from in here
options={options.from.options}
setOptions={(value) => handleSetOptions("from", "options", value)}
optionSelected={options.from.selected}

  1. 更新 state 时可以使用 previousState。 它与在您的示例中使用 state 相同,但它是异步和多挂钩情况的好习惯。
const switchFromToHandler = () => {
   setOptions((prevOptions) => ({
     from: prevOptions.to,
     to: prevOptions.from
   }));
 };

您可以检查codesandbox的更新版本

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM