如何根据 ReactJS 中的选择更改表单初始值和字段?
How to change Form initialValues and Fields based on selection in ReactJS?
我有一个这样的对象:
const initialValues = {
"Name": "",
"Price": "",
"Quantity": "",
"Details": {
"Light": "",
"Watering": "",
"Maintenance": "",
"WhereToGrow": "",
},
"BrowseImg": "",
"MainImg": "",
"Tags": "",
"Category": "Plant",
"SubCategory": ""
}
我在我的表单组件中使用 React Formik 库,我正在渲染 selection
& Details object
像这样:
<Grid item xs={6}>
<FormControl required>
<InputLabel id="demo-simple-select-required-label">Category</InputLabel>
<Select
labelId="demo-simple-select-required-label"
id="demo-simple-select-required"
value={values.Category !== "undefined" ? values.Category : ""}
label="Category *"
onChange={(e) => {
setFieldValue("Category", e.target.value);
}}
autoWidth
name="Category"
defaultValue=""
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={"Plant"}>Plant</MenuItem>
<MenuItem value={"Planters"}>Planters</MenuItem>
<MenuItem value={"Seeds"}>Seeds</MenuItem>
</Select>
</FormControl>
</Grid>
<FieldArray name="Details">
<Grid container spacing={2} sx={{ padding: "20px" }}>
<Grid item xs={12}>
<Typography>Details</Typography>
</Grid>
<Grid item xs={6}>
<TextFieldWrapper
name={
values.Category !== "Planters"
? `Details.Light`
: `Details.Material`
}
label={values.Category !== "Planters" ? `Light` : "Material"}
/>
</Grid>
<Grid item xs={6}>
<TextFieldWrapper
name={
values.Category !== "Planters"
? `Details.Watering`
: `Details.Build`
}
label={values.Category !== "Planters" ? `Watering` : "Build"}
/>
</Grid>
<Grid item xs={6}>
<TextFieldWrapper
name={
values.Category !== "Planters"
? `Details.Maintenance`
: `Details.PlanterHeight`
}
label={
values.Category !== "Planters" ? `Maintenance` : "Planter Height"
}
/>
</Grid>
<Grid item xs={6}>
<TextFieldWrapper
name={
values.Category !== "Planters"
? `Details.WhereToGrow`
: `Details.PlanterWidth`
}
label={
values.Category !== "Planters" ? `Where To Grow` : "Planter Width"
}
/>
</Grid>
</Grid>
</FieldArray>
现在,我需要将选择 "Planters"
时的 initialValues
对象更改为:
{
"Name": "",
"Price": "",
"Quantity": "",
"Details": {
"Material": "",
"Build": "",
"PlanterHeight": "",
"PlanterWidth": ""
},
"BrowseImg": "",
"MainImg": "",
"Tags": "",
"Category": "Planters",
"SubCategory": ""
}
我试过这样更改 initialValues
对象中的键名:
const Context = () => {
const { values } = useFormikContext();
useEffect(() => {
if (values.Category === "Planters") {
delete Object.assign(initialValues.Details, {
Material: initialValues.Details.Light,
})["Light"];
delete Object.assign(initialValues.Details, {
Build: initialValues.Details.Watering,
})["Watering"];
delete Object.assign(initialValues.Details, {
PlanterHeight: initialValues.Details.Maintenance,
})["Maintenance"];
delete Object.assign(initialValues.Details, {
PlanterWidth: initialValues.Details.WhereToGrow,
})["WhereToGrow"];
console.log("VALUES FORMIK CONTEXT", values);
console.log("INITIALVALUES", initialValues);
console.log(
"INITIAL VALUES DETAILS ",
initialValues.Details.Material
);
}
}, [values]);
return null;
};
当 values.Category
是 Planters 时更改名称会抛出 Formik 错误,如下所示:
Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component.
我该如何实现?提前致谢!
这不是很正确的方法,更改表单中的字段集对我来说不是个好主意 on-the-fly - 你在支持自己的混乱代码时会遇到问题,这个 useEffect
真是一团糟。
请记住,您用于 UI 的表单数据可能与您发送到后端的数据不同,并且可以在您发出请求之前进行转换。实际上 initialValues
用于初始化您的表单,不应从表单内部更改。我会创建通用字段集。
例如,我会像这样更改数据初始数据
// read-only array to render Select, add as much item as you like.
// In general, since it's just an array for options,
// it can be safely extracted to external constant
// and may not present in initialData.
Categories: [
{
name 'Planers',
selected: true,
},
{
name: 'Plants'
}
],
SelectedCategory: 'Planers', // since selected is true
Planters: {
"Material": "",
"Build": "",
"PlanterHeight": "",
"PlanterWidth": ""
},
Plants: {
"Light": "",
"Watering": "",
"Maintenance": "",
"WhereToGrow": "",
}
然后在你的表单中
// ...
const { values, handleChange, handleBlur } = useFormikContext();
<Select
onChange={handleChange} // or your own handler
onBlur={handleBlur}
name="SelectedCategory"
>
{values.Categories.map(({name, selected}) => (
<MenuItem
key={name}
selected={selected}
>
{name}
</MenuItem >
))}
</Select>
并且在同一组件中有两组或更多组字段。每组根据 SelectedCategory
值渲染。 SelectedCategory
更改时无需更改或重置字段集中的值 - 您可以在发送数据阶段删除或重置字段。
if (values.SelectedCategory === 'Planers') {
// render set of fields for Planers
}
if (values.SelectedCategory === 'Plants') {
// render set of fields for Plants
}
并且 onSubmit
您可以根据需要转换您的字段值:
<Formik
initialValues
onSubmit={(values, {resetForm}) => {
const transformedDataForAsyncRequest = yourCustomDataTransducer(values);
axios({
method: 'post',
data: transformedDataForAsyncRequest
})
.then(() => {resetForm()})
.catch((e) => {console.log(e)})
}
}
我有一个这样的对象:
const initialValues = {
"Name": "",
"Price": "",
"Quantity": "",
"Details": {
"Light": "",
"Watering": "",
"Maintenance": "",
"WhereToGrow": "",
},
"BrowseImg": "",
"MainImg": "",
"Tags": "",
"Category": "Plant",
"SubCategory": ""
}
我在我的表单组件中使用 React Formik 库,我正在渲染 selection
& Details object
像这样:
<Grid item xs={6}>
<FormControl required>
<InputLabel id="demo-simple-select-required-label">Category</InputLabel>
<Select
labelId="demo-simple-select-required-label"
id="demo-simple-select-required"
value={values.Category !== "undefined" ? values.Category : ""}
label="Category *"
onChange={(e) => {
setFieldValue("Category", e.target.value);
}}
autoWidth
name="Category"
defaultValue=""
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={"Plant"}>Plant</MenuItem>
<MenuItem value={"Planters"}>Planters</MenuItem>
<MenuItem value={"Seeds"}>Seeds</MenuItem>
</Select>
</FormControl>
</Grid>
<FieldArray name="Details">
<Grid container spacing={2} sx={{ padding: "20px" }}>
<Grid item xs={12}>
<Typography>Details</Typography>
</Grid>
<Grid item xs={6}>
<TextFieldWrapper
name={
values.Category !== "Planters"
? `Details.Light`
: `Details.Material`
}
label={values.Category !== "Planters" ? `Light` : "Material"}
/>
</Grid>
<Grid item xs={6}>
<TextFieldWrapper
name={
values.Category !== "Planters"
? `Details.Watering`
: `Details.Build`
}
label={values.Category !== "Planters" ? `Watering` : "Build"}
/>
</Grid>
<Grid item xs={6}>
<TextFieldWrapper
name={
values.Category !== "Planters"
? `Details.Maintenance`
: `Details.PlanterHeight`
}
label={
values.Category !== "Planters" ? `Maintenance` : "Planter Height"
}
/>
</Grid>
<Grid item xs={6}>
<TextFieldWrapper
name={
values.Category !== "Planters"
? `Details.WhereToGrow`
: `Details.PlanterWidth`
}
label={
values.Category !== "Planters" ? `Where To Grow` : "Planter Width"
}
/>
</Grid>
</Grid>
</FieldArray>
现在,我需要将选择 "Planters"
时的 initialValues
对象更改为:
{
"Name": "",
"Price": "",
"Quantity": "",
"Details": {
"Material": "",
"Build": "",
"PlanterHeight": "",
"PlanterWidth": ""
},
"BrowseImg": "",
"MainImg": "",
"Tags": "",
"Category": "Planters",
"SubCategory": ""
}
我试过这样更改 initialValues
对象中的键名:
const Context = () => {
const { values } = useFormikContext();
useEffect(() => {
if (values.Category === "Planters") {
delete Object.assign(initialValues.Details, {
Material: initialValues.Details.Light,
})["Light"];
delete Object.assign(initialValues.Details, {
Build: initialValues.Details.Watering,
})["Watering"];
delete Object.assign(initialValues.Details, {
PlanterHeight: initialValues.Details.Maintenance,
})["Maintenance"];
delete Object.assign(initialValues.Details, {
PlanterWidth: initialValues.Details.WhereToGrow,
})["WhereToGrow"];
console.log("VALUES FORMIK CONTEXT", values);
console.log("INITIALVALUES", initialValues);
console.log(
"INITIAL VALUES DETAILS ",
initialValues.Details.Material
);
}
}, [values]);
return null;
};
当 values.Category
是 Planters 时更改名称会抛出 Formik 错误,如下所示:
Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component.
我该如何实现?提前致谢!
这不是很正确的方法,更改表单中的字段集对我来说不是个好主意 on-the-fly - 你在支持自己的混乱代码时会遇到问题,这个 useEffect
真是一团糟。
请记住,您用于 UI 的表单数据可能与您发送到后端的数据不同,并且可以在您发出请求之前进行转换。实际上 initialValues
用于初始化您的表单,不应从表单内部更改。我会创建通用字段集。
例如,我会像这样更改数据初始数据
// read-only array to render Select, add as much item as you like.
// In general, since it's just an array for options,
// it can be safely extracted to external constant
// and may not present in initialData.
Categories: [
{
name 'Planers',
selected: true,
},
{
name: 'Plants'
}
],
SelectedCategory: 'Planers', // since selected is true
Planters: {
"Material": "",
"Build": "",
"PlanterHeight": "",
"PlanterWidth": ""
},
Plants: {
"Light": "",
"Watering": "",
"Maintenance": "",
"WhereToGrow": "",
}
然后在你的表单中
// ...
const { values, handleChange, handleBlur } = useFormikContext();
<Select
onChange={handleChange} // or your own handler
onBlur={handleBlur}
name="SelectedCategory"
>
{values.Categories.map(({name, selected}) => (
<MenuItem
key={name}
selected={selected}
>
{name}
</MenuItem >
))}
</Select>
并且在同一组件中有两组或更多组字段。每组根据 SelectedCategory
值渲染。 SelectedCategory
更改时无需更改或重置字段集中的值 - 您可以在发送数据阶段删除或重置字段。
if (values.SelectedCategory === 'Planers') {
// render set of fields for Planers
}
if (values.SelectedCategory === 'Plants') {
// render set of fields for Plants
}
并且 onSubmit
您可以根据需要转换您的字段值:
<Formik
initialValues
onSubmit={(values, {resetForm}) => {
const transformedDataForAsyncRequest = yourCustomDataTransducer(values);
axios({
method: 'post',
data: transformedDataForAsyncRequest
})
.then(() => {resetForm()})
.catch((e) => {console.log(e)})
}
}