使用 Formik 向导访问上一步中的值
Access values from previous step using Formik Wizard
我正在使用 Formik's Wizard component 创建一个多步骤表单。我不知道如何访问之前 <WizardStep />
的值。因此,例如,在第 1 步中有 firstName
和 lastName
的字段。是否可以在第二步中读取这些值?
所有值都在第二步中打印出来 onSubmit
但我如何在实际的 <WizardStep />
组件中访问它们?
基本上如何将道具传递给下一个<WizardStep />
import React, { useState } from 'react';
import { ErrorMessage, Field, Form, Formik } from 'formik';
import * as Yup from 'yup';
import { Debug } from './Debug';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
// Wizard is a single Formik instance whose children are each page of the
// multi-step form. The form is submitted on each forward transition (can only
// progress with valid input), whereas a backwards step is allowed with
// incomplete data. A snapshot of form state is used as initialValues after each
// transition. Each page has an optional submit handler, and the top-level
// submit is called when the final page is submitted.
const Wizard = ({ children, initialValues, onSubmit }) => {
const [stepNumber, setStepNumber] = useState(0);
const steps = React.Children.toArray(children);
const [snapshot, setSnapshot] = useState(initialValues);
const step = steps[stepNumber];
const totalSteps = steps.length;
const isLastStep = stepNumber === totalSteps - 1;
const next = values => {
setSnapshot(values);
setStepNumber(Math.min(stepNumber + 1, totalSteps - 1));
};
const previous = values => {
setSnapshot(values);
setStepNumber(Math.max(stepNumber - 1, 0));
};
const handleSubmit = async (values, bag) => {
if (step.props.onSubmit) {
await step.props.onSubmit(values, bag);
}
if (isLastStep) {
return onSubmit(values, bag);
} else {
bag.setTouched({});
next(values);
}
};
return (
<Formik
initialValues={snapshot}
onSubmit={handleSubmit}
validationSchema={step.props.validationSchema}
>
{formik => (
<Form>
<p>
Step {stepNumber + 1} of {totalSteps}
</p>
{step}
<div style={{ display: 'flex' }}>
{stepNumber > 0 && (
<button onClick={() => previous(formik.values)} type="button">
Back
</button>
)}
<div>
<button disabled={formik.isSubmitting} type="submit">
{isLastStep ? 'Submit' : 'Next'}
</button>
</div>
</div>
<Debug />
</Form>
)}
</Formik>
);
};
const WizardStep = ({ children }) => children;
const App = () => (
<div>
<h1>Formik Multistep Wizard</h1>
<Wizard
initialValues={{
email: '',
firstName: '',
lastName: '',
}}
onSubmit={async values =>
sleep(300).then(() => console.log('Wizard submit', values))
}
>
<WizardStep
onSubmit={() => console.log('Step1 onSubmit')}
validationSchema={Yup.object({
firstName: Yup.string().required('required'),
lastName: Yup.string().required('required'),
})}
>
<div>
<label htmlFor="firstName">First Name</label>
<Field
autoComplete="given-name"
component="input"
id="firstName"
name="firstName"
placeholder="First Name"
type="text"
/>
<ErrorMessage className="error" component="div" name="firstName" />
</div>
<div>
<label htmlFor="lastName">Last Name</label>
<Field
autoComplete="family-name"
component="input"
id="lastName"
name="lastName"
placeholder="Last Name"
type="text"
/>
<ErrorMessage className="error" component="div" name="lastName" />
</div>
</WizardStep>
<WizardStep
onSubmit={() => console.log('Step2 onSubmit')}
validationSchema={Yup.object({
email: Yup.string()
.email('Invalid email address')
.required('required'),
})}
>
<div>
<label htmlFor="email">Email</label>
<Field
autoComplete="email"
component="input"
id="email"
name="email"
placeholder="Email"
type="text"
/>
<ErrorMessage className="error" component="div" name="email" />
</div>
</WizardStep>
</Wizard>
</div>
);
export default App;
您可以为此目的使用 React context 或 Redux。这是使用 React Context 的方法。
第 1 步:创建一个“提供者”,它将成为所有向导步骤的状态容器。它非常简单,它包含一个状态。
const WizardContext = createContext();
const WizardProvider = ({ children }) => {
const [wizardState, setWizardState] = useState({});
const value = useMemo(() => [wizardState, setWizardState], [
wizardState,
setWizardState
]);
return (
<WizardContext.Provider value={value}>{children}</WizardContext.Provider>
);
};
const useWizardContext = () => useContext(WizardContext);
第 2 步:修复您的 App
组件并将其内容包装在提供程序中:
const App = () => (
<div>
<h1>Formik Multistep Wizard</h1>
<WizardProvider>
<AppContainer />
</WizardProvider>
</div>
);
第 3 步:按以下方式使用向导上下文。基本上,通过 setWizardState
函数控制全局状态并通过 wizardState
.
访问它
const AppContainer = () => {
const [wizardState, setWizardState] = useWizardContext();
return (
<Wizard .....>
<WizardStep onSubmit={(values) => {
setWizardState(values);
console.log("Step1 onSubmit");
}}></WizardStep>
<WizardStep onSubmit={() => {
console.log(wizardState);
console.log("Step2 onSubmit");
}}>
<div>{wizardState.firstName}</div>
</WizardStep>
</Wizard>
)
}
这是一个完整的代码沙箱示例:https://codesandbox.io/s/funny-galois-8xxwd?file=/src/App.js
我正在使用 Formik's Wizard component 创建一个多步骤表单。我不知道如何访问之前 <WizardStep />
的值。因此,例如,在第 1 步中有 firstName
和 lastName
的字段。是否可以在第二步中读取这些值?
所有值都在第二步中打印出来 onSubmit
但我如何在实际的 <WizardStep />
组件中访问它们?
基本上如何将道具传递给下一个<WizardStep />
import React, { useState } from 'react';
import { ErrorMessage, Field, Form, Formik } from 'formik';
import * as Yup from 'yup';
import { Debug } from './Debug';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
// Wizard is a single Formik instance whose children are each page of the
// multi-step form. The form is submitted on each forward transition (can only
// progress with valid input), whereas a backwards step is allowed with
// incomplete data. A snapshot of form state is used as initialValues after each
// transition. Each page has an optional submit handler, and the top-level
// submit is called when the final page is submitted.
const Wizard = ({ children, initialValues, onSubmit }) => {
const [stepNumber, setStepNumber] = useState(0);
const steps = React.Children.toArray(children);
const [snapshot, setSnapshot] = useState(initialValues);
const step = steps[stepNumber];
const totalSteps = steps.length;
const isLastStep = stepNumber === totalSteps - 1;
const next = values => {
setSnapshot(values);
setStepNumber(Math.min(stepNumber + 1, totalSteps - 1));
};
const previous = values => {
setSnapshot(values);
setStepNumber(Math.max(stepNumber - 1, 0));
};
const handleSubmit = async (values, bag) => {
if (step.props.onSubmit) {
await step.props.onSubmit(values, bag);
}
if (isLastStep) {
return onSubmit(values, bag);
} else {
bag.setTouched({});
next(values);
}
};
return (
<Formik
initialValues={snapshot}
onSubmit={handleSubmit}
validationSchema={step.props.validationSchema}
>
{formik => (
<Form>
<p>
Step {stepNumber + 1} of {totalSteps}
</p>
{step}
<div style={{ display: 'flex' }}>
{stepNumber > 0 && (
<button onClick={() => previous(formik.values)} type="button">
Back
</button>
)}
<div>
<button disabled={formik.isSubmitting} type="submit">
{isLastStep ? 'Submit' : 'Next'}
</button>
</div>
</div>
<Debug />
</Form>
)}
</Formik>
);
};
const WizardStep = ({ children }) => children;
const App = () => (
<div>
<h1>Formik Multistep Wizard</h1>
<Wizard
initialValues={{
email: '',
firstName: '',
lastName: '',
}}
onSubmit={async values =>
sleep(300).then(() => console.log('Wizard submit', values))
}
>
<WizardStep
onSubmit={() => console.log('Step1 onSubmit')}
validationSchema={Yup.object({
firstName: Yup.string().required('required'),
lastName: Yup.string().required('required'),
})}
>
<div>
<label htmlFor="firstName">First Name</label>
<Field
autoComplete="given-name"
component="input"
id="firstName"
name="firstName"
placeholder="First Name"
type="text"
/>
<ErrorMessage className="error" component="div" name="firstName" />
</div>
<div>
<label htmlFor="lastName">Last Name</label>
<Field
autoComplete="family-name"
component="input"
id="lastName"
name="lastName"
placeholder="Last Name"
type="text"
/>
<ErrorMessage className="error" component="div" name="lastName" />
</div>
</WizardStep>
<WizardStep
onSubmit={() => console.log('Step2 onSubmit')}
validationSchema={Yup.object({
email: Yup.string()
.email('Invalid email address')
.required('required'),
})}
>
<div>
<label htmlFor="email">Email</label>
<Field
autoComplete="email"
component="input"
id="email"
name="email"
placeholder="Email"
type="text"
/>
<ErrorMessage className="error" component="div" name="email" />
</div>
</WizardStep>
</Wizard>
</div>
);
export default App;
您可以为此目的使用 React context 或 Redux。这是使用 React Context 的方法。
第 1 步:创建一个“提供者”,它将成为所有向导步骤的状态容器。它非常简单,它包含一个状态。
const WizardContext = createContext();
const WizardProvider = ({ children }) => {
const [wizardState, setWizardState] = useState({});
const value = useMemo(() => [wizardState, setWizardState], [
wizardState,
setWizardState
]);
return (
<WizardContext.Provider value={value}>{children}</WizardContext.Provider>
);
};
const useWizardContext = () => useContext(WizardContext);
第 2 步:修复您的 App
组件并将其内容包装在提供程序中:
const App = () => (
<div>
<h1>Formik Multistep Wizard</h1>
<WizardProvider>
<AppContainer />
</WizardProvider>
</div>
);
第 3 步:按以下方式使用向导上下文。基本上,通过 setWizardState
函数控制全局状态并通过 wizardState
.
const AppContainer = () => {
const [wizardState, setWizardState] = useWizardContext();
return (
<Wizard .....>
<WizardStep onSubmit={(values) => {
setWizardState(values);
console.log("Step1 onSubmit");
}}></WizardStep>
<WizardStep onSubmit={() => {
console.log(wizardState);
console.log("Step2 onSubmit");
}}>
<div>{wizardState.firstName}</div>
</WizardStep>
</Wizard>
)
}
这是一个完整的代码沙箱示例:https://codesandbox.io/s/funny-galois-8xxwd?file=/src/App.js