У меня есть форма, отображаемая в модальном окне. Эта форма разделена на несколько вкладок. Один из них имеет два сгруппированных поля: выпадающий список стран и текстовое поле описания. Есть кнопка «Добавить», которая позволяет создать новое сгруппированное поле.
Проблема в том, что каждый раз, когда я заполнял текстовое поле, я терял фокус, потому что форма перерисовывалась. Я попытался переместить форму за пределы функции по умолчанию, но у меня все та же проблема.
Я также устанавливаю уникальные ключи для каждого элемента, но все же.
Я знаю, что есть много документации по этому поводу, но, несмотря на это, она не работает. Я мог бы настроить автофокус, но когда групповых полей больше одного, фокус будет идти на последний элемент.
Я использую Material UI (реакция 17)
Во всяком случае, ниже приведен код (который был усечен для лучшей наглядности):
function GeoForm (props) {
return(
<React.Fragment>
<Autocomplete
id = {"country"+props.i}
style = {{ width: 300 }}
options = {Object.keys(countries.getNames('fr')).map(e => ({code: e, label: countries.getNames('fr')[e]}))}
getOptionSelected = {(option, value) => (option.country === value.country)}
classes = {{
option: props.classes.option,
}}
defaultValue = {props.x.country}
key = {"country"+props.i}
name = "country"
onChange = {(e,v) => props.handleInputGeoCountryChange(e, v, props.i)}
getOptionLabel = {(option) => (option ? option.label : "")}
renderOption = {(option) => (
<React.Fragment>
{option.label}
</React.Fragment>
)}
renderInput = {(params) => (
<TextField
{...params}
label = "Choose a country"
variant = "outlined"
inputProps = {{
...params.inputProps,
autoComplete: 'new-password', // disable autocomplete and autofill
}}
/>
)}
/>
<TextField
id = {"destination"+props.i}
onChange = {e => props.handleInputGeoDestinationChange(e, props.i)}
defaultValue = {props.x.destination}
name = "destination"
key = {"destination"+props.i}
margin = "dense"
label = "Destination"
type = "text"
/>
{props.inputGeoList.length !== 1 && <button
className = "mr10"
onClick = {() => props.handleRemoveGeoItem(props.i)}>Delete</button>}
{props.inputGeoList.length - 1 === props.i &&
<Button
onClick = {props.handleAddGeoItem}
variant = "contained"
color = "primary"
//className = {classes.button}
endIcon = {<AddBoxIcon />}
>
Add
</Button>
}
</React.Fragment>
)
}
export default function modalInfo(props) {
const classes = useStyles();
const [openEditDialog, setOpenEditDialog] = React.useState(false);
const handleAddGeoItem = (e) => {
console.info(e);
setInputGeoList([...inputGeoList, { country: "", destination: "" }]);
};
// handle input change
const handleInputGeoCountryChange = (e, v, index) => {
const list = [...inputGeoList];
list[index]['country'] = v;
setInputGeoList(list);
};
const handleInputGeoDestinationChange = (e, index) => {
const { name, value } = e.target;
console.info(name);
const list = [...inputGeoList];
list[index][name] = value;
setInputGeoList(list);
console.info(inputGeoList)
};
// handle click event of the Remove button
const handleRemoveGeoItem = index => {
const list = [...inputGeoList];
list.splice(index, 1);
setInputGeoList(list);
};
const TabsEdit = (props) => {
return(
<div className = {classes.root}>
<form className = {classes.form} noValidate onSubmit = {onSubmit}>
<Tabs
orientation = "vertical"
variant = "scrollable"
value = {value}
onChange = {handleChangeEvent}
aria-label = "Vertical tabs example"
className = {classes.tabs}
>
[...]
<Tab label = "Geo-targeting" {...a11yProps(4)} disableRipple />
</Tabs>
[...]
</TabPanel>
<TabPanel value = {value} index = {4}>
{
inputGeoList.map((x, i)=>{
return(
<GeoForm
inputGeoList = {inputGeoList}
x = {x}
i = {i}
handleRemoveGeoItem = {handleRemoveGeoItem}
handleInputGeoDestinationChange = {handleInputGeoDestinationChange}
handleInputGeoCountryChange = {handleInputGeoCountryChange}
handleAddGeoItem = {handleAddGeoItem}
handleInputGeoDestinationChange = {handleInputGeoDestinationChange}
classes = {classes}
/>
)
})
}
</TabPanel>
<TabPanel value = {value} index = {5}>
Mobile-targeting
</TabPanel>
<DialogActions>
<Button onClick = {props.handleClose} color = "primary">
Annuler
</Button>
<Button type = "submit" color = "primary">
Enregistrer
</Button>
</DialogActions>
</form>
</div>
)
}
return (
<div>
<div>
<EditIconButton onClickEdit = {() => setOpenEditDialog(true)} />
</div>
<div>
<EditDialog open = {openEditDialog} handleClose = {() => setOpenEditDialog(false)} >
<TabsEdit/>
</EditDialog>
</div>
</div>
);
Любая помощь или предложение приветствуются. Спасибо
Спасибо. Я обновил ссылку codeandbox
Вкратце: ваш компонент TabsEdit
был определен внутри другого компонента, поэтому React каждый раз перемонтировал его как новый компонент, в результате чего сфокусированное состояние терялось. Этот Pastebin исправляет ваш код, сохраняет фокус при вводе.
NL;PR: я страдал от этой же проблемы в течение нескольких месяцев, не только реквизиты проверяются на соответствие, но и ссылка на память компонента. Поскольку ссылка на функцию компонента каждый раз разная, React обрабатывает ее как новый компонент, тем самым размонтируя предыдущий компонент, что приводит к потере состояния, в вашем случае фокуса.
Попробуйте иметь работающий фрагмент вашего кода в pastebin, например, используйте CodeSandBox, это пример с веб-страницы MUI: codeandbox.io/s/tuebl. Так легче ответить на ваш вопрос.