为什么我的表单数据值与提交的输入值不对应?
Why does my Form Data Value does not correspond to submitted Input Value?
我目前正在尝试创建一个动态 select/input 组件,您可以在其中从 select 选项中选择值或
通过 selecting“其他”select 选项在输入字段中键入您自己的值。
现在我被更新表单数据等同于 selected 选项/输入值的值所困扰。表单数据值始终保持初始/默认值。
App.js
...
export default function App() {
const methods = useForm({});
const { handleSubmit } = methods;
const customSalutationOptions = [
{ title: "Not specified", value: "null" },
{ title: "Male", value: "male" },
{ title: "Female", value: "female" }
];
const defaultValues = {
salutation: "null"
};
const onSubmit = (data) => {
console.log(data);
};
return (
<div className="App">
<FormProvider {...methods}>
<form onSubmit={handleSubmit(onSubmit)}>
<SelectOrInput
variant="outlined"
name={`contactPerson[0].salutation`}
defaultValue={defaultValues}
selectOptions={customSalutationOptions}
/>
<Button type="submit" color="primary" fullWidth variant="contained">
Submit
</Button>
</form>
</FormProvider>
</div>
);
}
components/SelectOrInput.tsx
...
type Props = {
name: string;
label: string;
selectOptions: [{ title: string; value: string }];
defaultValue: any;
shouldUnregister: boolean;
variant: "filled" | "outlined" | "standard";
};
export default function SelectOrInput({
name,
label,
selectOptions,
defaultValue,
shouldUnregister,
variant
}: Props) {
const classes = useStyles();
const { control } = useFormContext();
const [showCustomInput, setShowCustomInput] = useState(false);
const [value, setValue] = useState(selectOptions[0].value);
const additionalInput = [{ title: "Other", value: "" }];
const combindedOptions = selectOptions.concat(additionalInput);
const handleInputSelectChange = (
event: React.ChangeEvent<{ value: unknown }>
): void => {
const value = event.target.value as string;
if (value === "") {
const newState = !showCustomInput;
setShowCustomInput(newState);
console.log(value);
setValue(value);
} else {
setValue(value);
}
};
const resetCustomInputToSelect = (event: React.MouseEvent<HTMLElement>) => {
const newState = !showCustomInput;
setValue(combindedOptions[0].value);
setShowCustomInput(newState);
};
return (
<>
{showCustomInput ? (
<FormControl className={classes.input}>
<Controller
name={name}
control={control}
shouldUnregister={shouldUnregister}
render={({ field }) => (
<TextField
{...field}
label={label}
InputLabelProps={{ shrink: true }}
variant={variant}
placeholder="Other..."
autoFocus
type="text"
onChange={handleInputSelectChange}
value={value}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
size="small"
onClick={resetCustomInputToSelect}
id="custominput-closebutton"
>
<CloseIcon fontSize="small" />
</IconButton>
</InputAdornment>
)
}}
></TextField>
)}
/>
</FormControl>
) : (
<FormControl className={classes.input} variant={variant}>
<InputLabel id={`label-select-${label}`}>{label}</InputLabel>
<Controller
name={name}
defaultValue={defaultValue}
control={control}
shouldUnregister={shouldUnregister}
render={({ field }) => (
<Select
{...field}
label={label}
labelId={`label-select-${label}`}
value={value}
MenuProps={{
anchorOrigin: {
vertical: "bottom",
horizontal: "left"
},
getContentAnchorEl: null
}}
onChange={handleInputSelectChange}
>
{combindedOptions.map((option, index) => (
<MenuItem key={option.title} value={`${option.value}`}>
{option.title}
</MenuItem>
))}
</Select>
)}
/>
</FormControl>
)}
</>
);
}
...
为了给出一个更好的例子,我提供了一个 CSB:
您正在将 value
存储在它自己的 SelectOrInput
组件状态中。您需要将状态提升到父组件才能在父组件中获得价值。
- 在父组件中创建状态并使用默认值初始化并创建函数来更改它的值
const [inputValue, setInputValue] = useState(null);
const onChange = (value) => {
setInputValue(value);
};
- 在
SelectOrInput
组件中传递 onChange
函数,并在值更改时调用 onChange
函数
<SelectOrInput
...
onChange={onChange}
/>
// call onChange in handleInputSelectChange method
const handleInputSelectChange = (
event: React.ChangeEvent<{ value: unknown }>
): void => {
const value = event.target.value as string;
if (value === "") {
const newState = !showCustomInput;
setShowCustomInput(newState);
setValue(value);
onChange(value);
} else {
setValue(value);
onChange(value);
}
};
在@Priyank Kachhela 的大力帮助下,我找到了答案。
通过提升状态到它最接近的共同祖先并删除child
组件内的任何Controller
组件。
App.js
- 在父组件中创建状态并使用默认值初始化并创建函数来更改它的值
const [inputValue, setInputValue] = useState("null");
const onSubmit = (data) => {
// Stringify Object to always see real value, not the value evaluated upon first expanding.
//
console.log(JSON.stringify(data, 4));
};
const onChange = (value) => {
setInputValue(value);
};
- 用
Controller
包装 SelectOrInput
并将 onChange
函数、value
以及默认值传递给 Controller
。然后使用render
方法,在SelectOrInput
组件上传播field
。
<Controller
name={`contactPerson[0].salutation`}
defaultValue={defaultValues.salutation}
onChange={onChange}
value={inputValue}
control={control}
render={({ field }) => (
<SelectOrInput
{...field}
variant="outlined"
selectOptions={customSalutationOptions}
/>
)}
/>
components/SelectOrInput.js
- Bubble / (Call)
onChange
每当从 Child-(SelectOrInput) 组件中更改值时的事件处理程序。
const handleInputSelectChange = (
event: React.ChangeEvent<{ value: unknown }>
): void => {
const value = event.target.value as string;
if (value === "") {
const newState = !showCustomInput;
setShowCustomInput(newState);
// Bubble / (Call) Event
onChange(value);
} else {
onChange(value);
}
};
const resetCustomInputToSelect = (event: React.MouseEvent<HTMLElement>) => {
const newState = !showCustomInput;
// Bubble / (Call) Event
onChange("null");
setShowCustomInput(newState);
};
- 从 'SelectOrInput'
中删除组件内部输入状态
工作示例
在 Gist 中捕获的修订
https://gist.github.com/kkroeger93/1e4c0fe993f1745a34fb5717ee2ff545/revisions
我目前正在尝试创建一个动态 select/input 组件,您可以在其中从 select 选项中选择值或 通过 selecting“其他”select 选项在输入字段中键入您自己的值。
现在我被更新表单数据等同于 selected 选项/输入值的值所困扰。表单数据值始终保持初始/默认值。
App.js
...
export default function App() {
const methods = useForm({});
const { handleSubmit } = methods;
const customSalutationOptions = [
{ title: "Not specified", value: "null" },
{ title: "Male", value: "male" },
{ title: "Female", value: "female" }
];
const defaultValues = {
salutation: "null"
};
const onSubmit = (data) => {
console.log(data);
};
return (
<div className="App">
<FormProvider {...methods}>
<form onSubmit={handleSubmit(onSubmit)}>
<SelectOrInput
variant="outlined"
name={`contactPerson[0].salutation`}
defaultValue={defaultValues}
selectOptions={customSalutationOptions}
/>
<Button type="submit" color="primary" fullWidth variant="contained">
Submit
</Button>
</form>
</FormProvider>
</div>
);
}
components/SelectOrInput.tsx
...
type Props = {
name: string;
label: string;
selectOptions: [{ title: string; value: string }];
defaultValue: any;
shouldUnregister: boolean;
variant: "filled" | "outlined" | "standard";
};
export default function SelectOrInput({
name,
label,
selectOptions,
defaultValue,
shouldUnregister,
variant
}: Props) {
const classes = useStyles();
const { control } = useFormContext();
const [showCustomInput, setShowCustomInput] = useState(false);
const [value, setValue] = useState(selectOptions[0].value);
const additionalInput = [{ title: "Other", value: "" }];
const combindedOptions = selectOptions.concat(additionalInput);
const handleInputSelectChange = (
event: React.ChangeEvent<{ value: unknown }>
): void => {
const value = event.target.value as string;
if (value === "") {
const newState = !showCustomInput;
setShowCustomInput(newState);
console.log(value);
setValue(value);
} else {
setValue(value);
}
};
const resetCustomInputToSelect = (event: React.MouseEvent<HTMLElement>) => {
const newState = !showCustomInput;
setValue(combindedOptions[0].value);
setShowCustomInput(newState);
};
return (
<>
{showCustomInput ? (
<FormControl className={classes.input}>
<Controller
name={name}
control={control}
shouldUnregister={shouldUnregister}
render={({ field }) => (
<TextField
{...field}
label={label}
InputLabelProps={{ shrink: true }}
variant={variant}
placeholder="Other..."
autoFocus
type="text"
onChange={handleInputSelectChange}
value={value}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
size="small"
onClick={resetCustomInputToSelect}
id="custominput-closebutton"
>
<CloseIcon fontSize="small" />
</IconButton>
</InputAdornment>
)
}}
></TextField>
)}
/>
</FormControl>
) : (
<FormControl className={classes.input} variant={variant}>
<InputLabel id={`label-select-${label}`}>{label}</InputLabel>
<Controller
name={name}
defaultValue={defaultValue}
control={control}
shouldUnregister={shouldUnregister}
render={({ field }) => (
<Select
{...field}
label={label}
labelId={`label-select-${label}`}
value={value}
MenuProps={{
anchorOrigin: {
vertical: "bottom",
horizontal: "left"
},
getContentAnchorEl: null
}}
onChange={handleInputSelectChange}
>
{combindedOptions.map((option, index) => (
<MenuItem key={option.title} value={`${option.value}`}>
{option.title}
</MenuItem>
))}
</Select>
)}
/>
</FormControl>
)}
</>
);
}
...
为了给出一个更好的例子,我提供了一个 CSB:
您正在将 value
存储在它自己的 SelectOrInput
组件状态中。您需要将状态提升到父组件才能在父组件中获得价值。
- 在父组件中创建状态并使用默认值初始化并创建函数来更改它的值
const [inputValue, setInputValue] = useState(null);
const onChange = (value) => {
setInputValue(value);
};
- 在
SelectOrInput
组件中传递onChange
函数,并在值更改时调用onChange
函数
<SelectOrInput
...
onChange={onChange}
/>
// call onChange in handleInputSelectChange method
const handleInputSelectChange = (
event: React.ChangeEvent<{ value: unknown }>
): void => {
const value = event.target.value as string;
if (value === "") {
const newState = !showCustomInput;
setShowCustomInput(newState);
setValue(value);
onChange(value);
} else {
setValue(value);
onChange(value);
}
};
在@Priyank Kachhela 的大力帮助下,我找到了答案。
通过提升状态到它最接近的共同祖先并删除child
组件内的任何Controller
组件。
App.js
- 在父组件中创建状态并使用默认值初始化并创建函数来更改它的值
const [inputValue, setInputValue] = useState("null");
const onSubmit = (data) => {
// Stringify Object to always see real value, not the value evaluated upon first expanding.
//
console.log(JSON.stringify(data, 4));
};
const onChange = (value) => {
setInputValue(value);
};
- 用
Controller
包装SelectOrInput
并将onChange
函数、value
以及默认值传递给Controller
。然后使用render
方法,在SelectOrInput
组件上传播field
。
<Controller
name={`contactPerson[0].salutation`}
defaultValue={defaultValues.salutation}
onChange={onChange}
value={inputValue}
control={control}
render={({ field }) => (
<SelectOrInput
{...field}
variant="outlined"
selectOptions={customSalutationOptions}
/>
)}
/>
components/SelectOrInput.js
- Bubble / (Call)
onChange
每当从 Child-(SelectOrInput) 组件中更改值时的事件处理程序。
const handleInputSelectChange = (
event: React.ChangeEvent<{ value: unknown }>
): void => {
const value = event.target.value as string;
if (value === "") {
const newState = !showCustomInput;
setShowCustomInput(newState);
// Bubble / (Call) Event
onChange(value);
} else {
onChange(value);
}
};
const resetCustomInputToSelect = (event: React.MouseEvent<HTMLElement>) => {
const newState = !showCustomInput;
// Bubble / (Call) Event
onChange("null");
setShowCustomInput(newState);
};
- 从 'SelectOrInput' 中删除组件内部输入状态
工作示例
在 Gist 中捕获的修订
https://gist.github.com/kkroeger93/1e4c0fe993f1745a34fb5717ee2ff545/revisions