reactjs - MUI异步自动完成和react-hook-form:表单无法访问自动完成值
问题描述
我有一个使用 Material UI 和 react-hook-form 的多步骤 (2) 表单。第一步,我要求输入字符串(Q1)和地址(Q2)。然后,用户点击下一步进入第二步。
此时,我使用反应上下文将数据保存在全局状态并将其值传递到表单上的步骤 2,但是只有 Q1 的值被正确保存。Q2 的值保存为undefined
。
简化代码
第 1 步页面
//imports here
const StepOne= () => {
const { setValues } = useData();
const methods = useForm({
mode: "all",
shouldUnregister: true
});
const { handleSubmit, control } = methods;
const history = useHistory();
const onSubmit = async (data) => {
setValues({ address: data.address, other: data.other });
history.push("/step2");
};
return (
<MainConatiner>
<Typography variant="h4" component="h2" gutterBottom>
Step one
</Typography>
<FormProvider {...methods}>
<form noValidate onSubmit={handleSubmit(onSubmit)} autoComplete="off">
<AddressInput label="Address" name="address" control={control} />
<Input name="other" label="Other input" control={control} />
<Box mt={2}>
<Button type="submit" size="large" variant="contained">
next
</Button>
</Box>
</form>
</FormProvider>
</MainConatiner>
);
};
自动完成组件
const AddressInput = ({ control, name, ...rest }) => {
const [value, setValue] = React.useState(null);
const [inputValue, setInputValue] = React.useState("");
const [options, setOptions] = React.useState([]);
const fetch = React.useMemo(
() =>
throttle((request) => {
const getData = getAddress(request?.input);
getData.then((res) => {
setOptions(JSON.parse(res)?.candidates);
return JSON.parse(res).candidates;
});
}, 200),
[]
);
React.useEffect(() => {
let active = true;
if (inputValue === "") {
setOptions(value ? [value] : []);
return undefined;
}
fetch({ input: inputValue }, (results) => {
if (active) {
let newOptions = [];
if (value) {
newOptions = [value];
}
if (results) {
newOptions = [...newOptions, ...results];
}
setOptions(newOptions);
}
});
return () => {
active = false;
};
}, [value, inputValue, fetch]);
return (
<Controller
name={name}
control={control}
render={({ field }) => (
<Autocomplete
{...field}
id="address"
getOptionLabel={(option) =>
typeof option === "string" ? option : option.address
}
filterOptions={(x) => x}
options={options}
autoComplete
includeInputInList
filterSelectedOptions
value={value}
onChange={(event, newValue) => {
console.log(newValue);
setOptions(newValue ? [newValue, ...options] : options);
setValue(newValue);
}}
onInputChange={(event, newInputValue) => {
setInputValue(newInputValue);
}}
renderInput={(params) => (
<TextField {...rest} {...params} variant="outlined" fullWidth />
)}
renderOption={(option) => <span>{option.address}</span>}
/>
)}
/>
);
};
你可以在这里找到完整的代码:https ://codesandbox.io/s/multi-step-form-4cgj9?file=/src/pages/Home.js:363-1313
有任何想法吗?
解决方案
有两个问题:
- 您在
{...field}
上展开Autocomplete
,这包括onChange
您稍后覆盖的字段。因此你必须调用field.onChange(newValue)
你自己的内在onChange
。目前 react-hook-forms 永远不会设置为新值。 - 您应该添加 ,
getOptionSelected
以便Autocomplete
它可以比较选项并检查选择了哪个选项。
然后AddressInput
看起来像这样:
import React from "react";
import TextField from "@material-ui/core/TextField";
import Autocomplete from "@material-ui/lab/Autocomplete";
import throttle from "lodash/throttle";
import getAddress from "./api/getAddress";
import { Controller } from "react-hook-form";
const AddressInput = ({ control, name, ...rest }) => {
const [value, setValue] = React.useState(null);
const [inputValue, setInputValue] = React.useState("");
const [options, setOptions] = React.useState([]);
const fetch = React.useMemo(
() =>
throttle((request) => {
const getData = getAddress(request?.input);
getData.then((res) => {
setOptions(JSON.parse(res)?.candidates);
return JSON.parse(res).candidates;
});
}, 200),
[]
);
React.useEffect(() => {
let active = true;
if (inputValue === "") {
setOptions(value ? [value] : []);
return undefined;
}
fetch({ input: inputValue }, (results) => {
if (active) {
let newOptions = [];
if (value) {
newOptions = [value];
}
if (results) {
newOptions = [...newOptions, ...results];
}
setOptions(newOptions);
}
});
return () => {
active = false;
};
}, [value, inputValue, fetch]);
return (
<Controller
name={name}
control={control}
render={({ field }) => (
<Autocomplete
{...field}
id="address"
getOptionLabel={(option) =>
typeof option === "string" ? option : option.address
}
// Add this prop to help with identifying the selected option
getOptionSelected={(optionA, optionB) =>
optionA.address === optionB.address
}
filterOptions={(x) => x}
options={options}
autoComplete
includeInputInList
filterSelectedOptions
value={value}
onChange={(event, newValue) => {
console.log('newValue', newValue);
setOptions(newValue ? [newValue, ...options] : options);
setValue(newValue);
// Actually change the state of react-hook-forms
field.onChange(newValue);
}}
onInputChange={(event, newInputValue) => {
setInputValue(newInputValue);
}}
renderInput={(params) => (
<TextField {...rest} {...params} variant="outlined" fullWidth />
)}
renderOption={(option) => <span>{option.address}</span>}
/>
)}
/>
);
};
export default AddressInput;
更新的代码框
推荐阅读
- javascript - 如何从数组中获取随机图像并在警报中显示图像名称而不使用 JQUERY 重复相同图像
- python - python中的基本面向对象实现
- wolkenkit - 使用 wolkenkit 使用流的示例应用程序
- python - 多次预测后如何计算错误分类率列?
- java - 如何正确地用斜线分割字符串
- php - 将 .php 文件转换为 .html 以获取 pdf 视图
- android - 如何将毫秒转换为秒
- kubernetes - 将 Cockroach DB 从本地机器迁移到 GCP Kubernetes Engine
- php - 使用单选按钮获得正确答案 - 在线测验
- xpath - 量角器期望页面中不存在