繁体   English   中英

MUI 异步自动完成和 react-hook-form:表单无法访问自动完成值

[英]MUI async Autocomplete and react-hook-form: Form can't access Autocomplete value

我有一个使用 Material UI 和 react-hook-form 的多步骤 (2) 表单。 第一步,我要求输入字符串(Q1)和地址(Q2) 然后,用户点击下一步进入第二步。

此时,我使用反应上下文将数据保存在全局 state 中,并将其值传递给表单上的步骤 2,但只有 Q1 的值被正确保存。 Q2 的值保存为undefined

简化代码

第一步页面

//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

有任何想法吗?

有两个问题:

  1. 您正在将{...field}传播到Autocomplete上,这包括您稍后覆盖的onChange字段。 因此,您必须在自己的onChange中调用field.onChange(newValue) 目前 react-hook-forms 永远不会设置为新值。
  2. 您应该将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;

更新代码和框

暂无
暂无

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

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