如何使用实现 useField() 的组件在多步骤 Formik 表单中设置值
How to set values in a multi-step Formik form with components that implement useField()
我正在将 multi-step wizard example with Material-UI components and it works well with the useField()
hook but I cannot figure out how to bring setFieldValue()
实施到范围中,因此我可以从向导步骤中使用它。
我看到了使用 connect()
higher-order component 的建议,但我不知道该怎么做。
这是我的代码片段:CodeSandbox,用例:
向导步骤有一些可选字段,可以 shown/hidden 使用 Material-UI Switch
。我希望在关闭开关时清除可选字段中的值。
即
- 打开开关。
- 在评论字段中输入数据。
- 切换关闭。
- 评论值已清除。
- 打开开关。
- 评论字段为空。
希望有人能帮忙!谢谢。
我相信这就是您想要的:CodeSandbox。我分叉了你的 CodeSandbox。
我试图尽可能地遵循您的代码,但最终没有使用 WizardStep
。 step
变量返回一个 React 组件,它是 Formik
的子组件。 Formik
使用道具渲染,例如setFieldValue
,可以传给子代。为了将 setFieldValue
作为道具传递给 step
,我不得不使用 cloneElement()
(https://reactjs.org/docs/react-api.html#cloneelement),这允许我克隆 step
组件和如下添加道具。
// FormikWizard.js
<Formik
initialValues={snapshot}
onSubmit={handleSubmit}
validate={step.props.validate}
>
{(formik) => (
<Form>
<DialogContent className={classes.wizardDialogContent}>
<Stepper
className={classes.wizardDialogStepper}
activeStep={stepNumber}
alternativeLabel
>
{steps.map((step) => (
<Step key={step.props.name}>
<StepLabel>{step.props.name}</StepLabel>
</Step>
))}
</Stepper>
<Box
className={classes.wizardStepContent}
data-cy="wizardStepContent"
>
{React.cloneElement(step, {
setFieldValue: formik.setFieldValue
})}
</Box>
</DialogContent>
<DialogActions
className={classes.wizardDialogActions}
data-cy="wizardDialogActions"
>
<Button onClick={handleCancel} color="primary">
Cancel
</Button>
<Button
disabled={stepNumber <= 0}
onClick={() => handleBack(formik.values)}
color="primary"
>
Back
</Button>
<Button
disabled={formik.isSubmitting}
type="submit"
variant="contained"
color="primary"
>
{isFinalStep ? "Submit" : "Next"}
</Button>
</DialogActions>
</Form>
)}
</Formik>
为了访问子组件中的 setFieldValue
道具,在 App.js
中,我创建了一个名为 StepOne
的新组件,并用它来环绕输入,而不是使用 WizardStep
。现在我可以访问 setFieldValue
并在 handleOptionalChange
函数中使用它。
// App.js
import React, { useState } from "react";
import "./styles.css";
import { makeStyles } from "@material-ui/core/styles";
import Box from "@material-ui/core/Box";
import CssBaseline from "@material-ui/core/CssBaseline";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import FormGroup from "@material-ui/core/FormGroup";
import Switch from "@material-ui/core/Switch";
import FormikTextField from "./FormikTextField";
import { Wizard, WizardStep } from "./FormikWizard";
const useStyles = makeStyles((theme) => ({
content: {
display: "flex",
flexFlow: "column nowrap",
alignItems: "center",
width: "100%"
}
}));
const initialValues = {
forename: "",
surname: "",
optionalComments: ""
};
const StepOne = ({ setFieldValue }) => {
const classes = useStyles();
const [optional, setOptional] = useState(false);
const displayOptional = optional ? null : "none";
const handleOptionalChange = () => {
setFieldValue("optionalComments", "");
setOptional(!optional);
};
return (
<Box className={classes.content}>
<FormikTextField
fullWidth
size="small"
variant="outlined"
name="forename"
label="Forename"
type="text"
/>
<FormikTextField
fullWidth
size="small"
variant="outlined"
name="surname"
label="Surname"
type="text"
/>
<FormGroup>
<FormControlLabel
control={
<Switch
checked={optional}
onChange={handleOptionalChange}
name="optional"
color="primary"
/>
}
label="Optional"
/>
</FormGroup>
<FormikTextField
style={{ display: displayOptional }}
fullWidth
size="small"
variant="outlined"
name="optionalComments"
label="Comments"
type="text"
/>
</Box>
);
};
function App(props) {
return (
<>
<CssBaseline />
<Wizard
title="My Wizard"
open={true}
initialValues={initialValues}
onCancel={() => {
return;
}}
onSubmit={async (values) => {
console.log(JSON.stringify(values));
}}
>
<StepOne />
<StepTwo />
</Wizard>
</>
);
}
export default App;
备选
要在 Formik 中使用 setFieldValue
,最简单的方法是将所有输入元素放在 <Formik></Formik
标签中。您可以根据您正在进行的步骤有条件地呈现输入元素,如下所示。这使输入可以直接访问 setFieldValue
,因此您可以在 Switch
输入上调用 setFieldValue("optionalComments", "")
,这将清除每个切换的注释。虽然这可能意味着您的表格会更长,但我认为这不一定是坏事。
<Formik>
<Form>
{step === 1 && <div>
// Insert inputs here
</div>}
{step === 2 && <div>
<TextField
onChange={(event) => setFieldValue("someField", event.target.value)}
/>
<Switch
checked={optional}
onChange={() => {
setFieldValue("optionalComments", "");
setOptional(!optional);
}}
name="optional"
color="primary"
/>
</div>}
</Form>
</Formik>
前几天我遇到了这个 但由于无法正常工作而放弃了它。
它确实有效,但我对它是否是正确的方法有两种看法。
const handleOptionalChange = (form) => {
setOptional(!optional)
form.setFieldValue('optionalComments', '', false)
}
<FormGroup>
<FormControlLabel
control={
// As this element is not a Formik field, it has no access to the Formik context.
// Wrap with Field to gain access to the context.
<Field>
{({ field, form }) => (
<Switch
checked={optional}
onChange={() => handleOptionalChange(form)}
name="optional"
color="primary"
/>
)}
</Field>
}
label="Optional"
/>
</FormGroup>
我正在将 multi-step wizard example with Material-UI components and it works well with the useField()
hook but I cannot figure out how to bring setFieldValue()
实施到范围中,因此我可以从向导步骤中使用它。
我看到了使用 connect()
higher-order component 的建议,但我不知道该怎么做。
这是我的代码片段:CodeSandbox,用例:
向导步骤有一些可选字段,可以 shown/hidden 使用 Material-UI Switch
。我希望在关闭开关时清除可选字段中的值。
即
- 打开开关。
- 在评论字段中输入数据。
- 切换关闭。
- 评论值已清除。
- 打开开关。
- 评论字段为空。
希望有人能帮忙!谢谢。
我相信这就是您想要的:CodeSandbox。我分叉了你的 CodeSandbox。
我试图尽可能地遵循您的代码,但最终没有使用 WizardStep
。 step
变量返回一个 React 组件,它是 Formik
的子组件。 Formik
使用道具渲染,例如setFieldValue
,可以传给子代。为了将 setFieldValue
作为道具传递给 step
,我不得不使用 cloneElement()
(https://reactjs.org/docs/react-api.html#cloneelement),这允许我克隆 step
组件和如下添加道具。
// FormikWizard.js
<Formik
initialValues={snapshot}
onSubmit={handleSubmit}
validate={step.props.validate}
>
{(formik) => (
<Form>
<DialogContent className={classes.wizardDialogContent}>
<Stepper
className={classes.wizardDialogStepper}
activeStep={stepNumber}
alternativeLabel
>
{steps.map((step) => (
<Step key={step.props.name}>
<StepLabel>{step.props.name}</StepLabel>
</Step>
))}
</Stepper>
<Box
className={classes.wizardStepContent}
data-cy="wizardStepContent"
>
{React.cloneElement(step, {
setFieldValue: formik.setFieldValue
})}
</Box>
</DialogContent>
<DialogActions
className={classes.wizardDialogActions}
data-cy="wizardDialogActions"
>
<Button onClick={handleCancel} color="primary">
Cancel
</Button>
<Button
disabled={stepNumber <= 0}
onClick={() => handleBack(formik.values)}
color="primary"
>
Back
</Button>
<Button
disabled={formik.isSubmitting}
type="submit"
variant="contained"
color="primary"
>
{isFinalStep ? "Submit" : "Next"}
</Button>
</DialogActions>
</Form>
)}
</Formik>
为了访问子组件中的 setFieldValue
道具,在 App.js
中,我创建了一个名为 StepOne
的新组件,并用它来环绕输入,而不是使用 WizardStep
。现在我可以访问 setFieldValue
并在 handleOptionalChange
函数中使用它。
// App.js
import React, { useState } from "react";
import "./styles.css";
import { makeStyles } from "@material-ui/core/styles";
import Box from "@material-ui/core/Box";
import CssBaseline from "@material-ui/core/CssBaseline";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import FormGroup from "@material-ui/core/FormGroup";
import Switch from "@material-ui/core/Switch";
import FormikTextField from "./FormikTextField";
import { Wizard, WizardStep } from "./FormikWizard";
const useStyles = makeStyles((theme) => ({
content: {
display: "flex",
flexFlow: "column nowrap",
alignItems: "center",
width: "100%"
}
}));
const initialValues = {
forename: "",
surname: "",
optionalComments: ""
};
const StepOne = ({ setFieldValue }) => {
const classes = useStyles();
const [optional, setOptional] = useState(false);
const displayOptional = optional ? null : "none";
const handleOptionalChange = () => {
setFieldValue("optionalComments", "");
setOptional(!optional);
};
return (
<Box className={classes.content}>
<FormikTextField
fullWidth
size="small"
variant="outlined"
name="forename"
label="Forename"
type="text"
/>
<FormikTextField
fullWidth
size="small"
variant="outlined"
name="surname"
label="Surname"
type="text"
/>
<FormGroup>
<FormControlLabel
control={
<Switch
checked={optional}
onChange={handleOptionalChange}
name="optional"
color="primary"
/>
}
label="Optional"
/>
</FormGroup>
<FormikTextField
style={{ display: displayOptional }}
fullWidth
size="small"
variant="outlined"
name="optionalComments"
label="Comments"
type="text"
/>
</Box>
);
};
function App(props) {
return (
<>
<CssBaseline />
<Wizard
title="My Wizard"
open={true}
initialValues={initialValues}
onCancel={() => {
return;
}}
onSubmit={async (values) => {
console.log(JSON.stringify(values));
}}
>
<StepOne />
<StepTwo />
</Wizard>
</>
);
}
export default App;
备选
要在 Formik 中使用 setFieldValue
,最简单的方法是将所有输入元素放在 <Formik></Formik
标签中。您可以根据您正在进行的步骤有条件地呈现输入元素,如下所示。这使输入可以直接访问 setFieldValue
,因此您可以在 Switch
输入上调用 setFieldValue("optionalComments", "")
,这将清除每个切换的注释。虽然这可能意味着您的表格会更长,但我认为这不一定是坏事。
<Formik>
<Form>
{step === 1 && <div>
// Insert inputs here
</div>}
{step === 2 && <div>
<TextField
onChange={(event) => setFieldValue("someField", event.target.value)}
/>
<Switch
checked={optional}
onChange={() => {
setFieldValue("optionalComments", "");
setOptional(!optional);
}}
name="optional"
color="primary"
/>
</div>}
</Form>
</Formik>
前几天我遇到了这个
const handleOptionalChange = (form) => {
setOptional(!optional)
form.setFieldValue('optionalComments', '', false)
}
<FormGroup>
<FormControlLabel
control={
// As this element is not a Formik field, it has no access to the Formik context.
// Wrap with Field to gain access to the context.
<Field>
{({ field, form }) => (
<Switch
checked={optional}
onChange={() => handleOptionalChange(form)}
name="optional"
color="primary"
/>
)}
</Field>
}
label="Optional"
/>
</FormGroup>