MUI 自动完成和 react-hook-form 不显示带有获取数据的选定选项
MUI Autocomplete and react-hook-form not displaying selected option with fetched data
我在来自反应钩子表单的表单中有一个 MUI 自动完成功能,它在填写表单时工作正常,但是当我想显示填充了获取数据的表单时,MUI 自动完成功能仅在两次呈现后显示所选选项。
我认为这与 useEffect 和 reset(来自反应挂钩形式)有关,因为选项为静态的自动完成功能可以正常工作,但我还必须从我的 API 中获取选项的自动完成功能才有效在 useEffect 第二次运行后正确。
我无法重现 codesandbox,因为它是一个消耗真实 api 的大型项目,但如果需要,我可以提供更多信息。如果有人可以帮助我,请提前致谢。
我选择要在表单内可视化的项目的页面:
const People: React.FC = () => {
const [show, setShow] = useState(false);
const [modalData, setModalData] = useState<PeopleProps>({} as PeopleProps);
async function showCustomer(id: string) {
await api
.get(`people/${id}`)
.then((response) => {
setModalData(response.data);
setShow(true);
})
.catch((error) => toast.error('Error')
)
}
return (
<>
{...} // there's a table here with items that onClick will fire showCustomer()
<Modal
data={modalData}
visible={show}
/>
</>
);
};
我在模态框内的表单:
const Modal: React.FC<ModalProps> = ({data, visible}) => {
const [situations, setSituations] = useState<Options[]>([]);
const methods = useForm<PeopleProps>({defaultValues: data});
const {reset} = methods;
/* FETCH POSSIBLE SITUATIONS FROM API*/
useEffect(() => {
api
.get('situations')
.then((situation) => setSituations(situation.data.data))
.catch((error) => toast.error('Error'));
}, [visible]);
/* RESET FORM TO POPULATE WITH FETCHED DATA */
useEffect(() => reset(data), [visible]);
return (
<Dialog open={visible}>
<FormProvider {...methods}>
<DialogContent>
<ComboBox
name="situation_id"
label="Situação"
options={situations.map((item) => ({
id: item.id,
text: item.description
}))}
/>
</DialogContent>
</FormProvider>
</Dialog>
);
};
export default Modal;
组合框组件:
const ComboBox: React.FC<ComboProps> = ({name, options, ...props}) => {
const {control, getValues} = useFormContext();
return (
<Controller
name={`${name}`}
control={control}
render={(props) => (
<Autocomplete
{...props}
options={options}
getOptionLabel={(option) => option.text}
getOptionSelected={(option, value) => option.id === value.id}
defaultValue={options.find(
(item) => item.id === getValues(`${name}`)
)}
renderInput={(params) => (
<TextField
variant="outlined"
{...props}
{...params}
/>
)}
onChange={(event, data) => {
props.field.onChange(data?.id);
}}
/>
)}
/>
);
};
export default ComboBox;
我认为你在这里简化了一些事情:
- 有条件地渲染
<Modal />
组件,这样您就不必在不使用它时渲染它。
- 您不应为
<Autocomplete />
组件设置 defaultValue
,因为 RHF 会为您管理状态。因此,如果您要重置表单,RHF 将为此控件使用该新值。
- 只使用其中一个获取的选项作为
<Autocomplete />
的 current/default 值要容易得多 - 因此不必在每次发生更改(并传递)时迭代所有选项situation_id 作为此控件的值),只需在获取情况后找到默认选项并使用此值重置表单即可。在 CodeSandbox 中,我将您的控件从“situation_id”重命名为“situation”。这样你只需要在 <Modal />
的第一次渲染上映射“situation_id”,就在你将编辑的值发送到你的 api 之前保存。
我制作了一个小型 CodeSandbox 试图重现您的用例,看看:
mui@v4
mui@v5
另一件重要的事情:只有当你有深度嵌套的控件时,你才应该使用 useFormContext
,否则只需将控件传递给你的 <ComboBox />
组件。与使用 FormProvider
一样,如果表单变得更大更复杂,它可能会影响应用程序的性能。来自 documentation:
React Hook Form's FormProvider is built upon React's Context API. It solves the problem where data is passed through the component tree without having to pass props down manually at every level. This also causes the component tree to trigger a re-render when React Hook Form triggers a state update
我在来自反应钩子表单的表单中有一个 MUI 自动完成功能,它在填写表单时工作正常,但是当我想显示填充了获取数据的表单时,MUI 自动完成功能仅在两次呈现后显示所选选项。
我认为这与 useEffect 和 reset(来自反应挂钩形式)有关,因为选项为静态的自动完成功能可以正常工作,但我还必须从我的 API 中获取选项的自动完成功能才有效在 useEffect 第二次运行后正确。
我无法重现 codesandbox,因为它是一个消耗真实 api 的大型项目,但如果需要,我可以提供更多信息。如果有人可以帮助我,请提前致谢。
我选择要在表单内可视化的项目的页面:
const People: React.FC = () => {
const [show, setShow] = useState(false);
const [modalData, setModalData] = useState<PeopleProps>({} as PeopleProps);
async function showCustomer(id: string) {
await api
.get(`people/${id}`)
.then((response) => {
setModalData(response.data);
setShow(true);
})
.catch((error) => toast.error('Error')
)
}
return (
<>
{...} // there's a table here with items that onClick will fire showCustomer()
<Modal
data={modalData}
visible={show}
/>
</>
);
};
我在模态框内的表单:
const Modal: React.FC<ModalProps> = ({data, visible}) => {
const [situations, setSituations] = useState<Options[]>([]);
const methods = useForm<PeopleProps>({defaultValues: data});
const {reset} = methods;
/* FETCH POSSIBLE SITUATIONS FROM API*/
useEffect(() => {
api
.get('situations')
.then((situation) => setSituations(situation.data.data))
.catch((error) => toast.error('Error'));
}, [visible]);
/* RESET FORM TO POPULATE WITH FETCHED DATA */
useEffect(() => reset(data), [visible]);
return (
<Dialog open={visible}>
<FormProvider {...methods}>
<DialogContent>
<ComboBox
name="situation_id"
label="Situação"
options={situations.map((item) => ({
id: item.id,
text: item.description
}))}
/>
</DialogContent>
</FormProvider>
</Dialog>
);
};
export default Modal;
组合框组件:
const ComboBox: React.FC<ComboProps> = ({name, options, ...props}) => {
const {control, getValues} = useFormContext();
return (
<Controller
name={`${name}`}
control={control}
render={(props) => (
<Autocomplete
{...props}
options={options}
getOptionLabel={(option) => option.text}
getOptionSelected={(option, value) => option.id === value.id}
defaultValue={options.find(
(item) => item.id === getValues(`${name}`)
)}
renderInput={(params) => (
<TextField
variant="outlined"
{...props}
{...params}
/>
)}
onChange={(event, data) => {
props.field.onChange(data?.id);
}}
/>
)}
/>
);
};
export default ComboBox;
我认为你在这里简化了一些事情:
- 有条件地渲染
<Modal />
组件,这样您就不必在不使用它时渲染它。 - 您不应为
<Autocomplete />
组件设置defaultValue
,因为 RHF 会为您管理状态。因此,如果您要重置表单,RHF 将为此控件使用该新值。 - 只使用其中一个获取的选项作为
<Autocomplete />
的 current/default 值要容易得多 - 因此不必在每次发生更改(并传递)时迭代所有选项situation_id 作为此控件的值),只需在获取情况后找到默认选项并使用此值重置表单即可。在 CodeSandbox 中,我将您的控件从“situation_id”重命名为“situation”。这样你只需要在<Modal />
的第一次渲染上映射“situation_id”,就在你将编辑的值发送到你的 api 之前保存。
我制作了一个小型 CodeSandbox 试图重现您的用例,看看:
mui@v4
mui@v5
另一件重要的事情:只有当你有深度嵌套的控件时,你才应该使用 useFormContext
,否则只需将控件传递给你的 <ComboBox />
组件。与使用 FormProvider
一样,如果表单变得更大更复杂,它可能会影响应用程序的性能。来自 documentation:
React Hook Form's FormProvider is built upon React's Context API. It solves the problem where data is passed through the component tree without having to pass props down manually at every level. This also causes the component tree to trigger a re-render when React Hook Form triggers a state update