如何为 Formik 和 Yup 创建的 Form 设置初始值
我有一个关于如何将初始值设置到由 Formik 和 Yup 创建的表单中的问题。我想让用户在更新页面上编辑他们的测验,所以我想将用户更新的测验值插入到每个输入中。
我可以获取一个作为 q 的测验对象,并通过登录控制台进行检查,但出现无法读取问题属性的错误。
import { useParams } from 'react-router-dom';
import { collection, query, setDoc, addDoc, getDoc, doc } from 'firebase/firestore';
import db from '../config/firebase';
import { useState, useEffect } from 'react';
import { Formik, Form, Field, FieldArray, ErrorMessage } from 'formik';
import * as Yup from 'yup';
import { ioRemoveCircleSharp } from '../icons/icons';
Yup.addMethod(Yup.array, 'unique', function (message, mapper = a => a) {
return this.test('unique', message, function (list) {
return list.length === new Set(list.map(mapper)).size;
});
});
const quizSchema = Yup.object().shape({
question: Yup.string()
.min(10, 'Too Short!')
.max(250, 'Too Long!')
.required('Required'),
answers: Yup.array()
.of(Yup.string().max(50, 'Too Long!').required('Required'))
.unique('Duplicate answers are not allowed')
.min(2, `Minimum of 2 answers`),
correctAnswer: Yup.number('only numbers are allowed')
.min(1, 'type more than 1 please')
.max(4, 'type less than 5')
.required('Required'),
category: Yup.string().required('Required'),
createdAt: Yup.date(),
likes: Yup.number(),
});
const QuizEdit = () => {
const { id } = useParams();
const [focused, setFocused] = useState(false);
const [submitBtnHover, setSubmitBtnHover] = useState(false);
const [q, setQuiz] = useState()
useEffect(() => {
const getData = async () => {
const snap = await getDoc(doc(db, 'quizzes', id));
console.log(snap.data().answers);
const quiz = snap.data();
console.log(quiz);
setQuiz(quiz)
};
getData();
}, []);
console.log(`q => ${q}`)
return (
<div className='formikNewQuiz'>
<Formik
initialValues={{
question: q["question"],
answers: ['', ''],
correctAnswer: '',
category: '',
createdAt: new Date(),
likes: 0,
}}
validateOnChange
validationSchema={quizSchema}
onSubmit={async (values, { resetForm }) => {
// same shape as initial values
console.log(values);
const quizCollectionRef = collection(db, 'quizzes');
const payload = values;
await addDoc(quizCollectionRef, payload);
// values["question"] = "";
// values["answers"] = ["", ""];
// values.correctAnswer = ""
// values.category = ""
resetForm();
}}
>
{({ errors, touched, values, q }) => (
<Form>
<div style={labelInputContainer}>
<label htmlFor='question' style={label}>
Question
</label>
<Field
name='question'
onFocus={() => {
setFocused('question');
}}
onBlur={() => {
setFocused('');
}}
style={focused === 'question' ? focusStyle : quizFormInputText}
/>
{errors.question && touched.question ? (
<div style={quizFormErrMsg}>{errors.question}</div>
) : null}
</div>
<FieldArray
name='answers'
style={quizFormInputText}
render={arrayHelpers => (
<div style={labelInputContainer}>
<label htmlFor='answers1' style={label}>
Answers
</label>
{errors.answers &&
touched.answers &&
errors.answers === 'Duplicate answers are not allowed' ? (
<div style={quizFormErrMsg}>{errors.answers}</div>
) : null}
{values.answers && values.answers.length > 0
? values.answers.map((answer, index) => (
<div key={index} style={answerContainer}>
<div style={indexAnswerIconContainer}>
<span style={answerIndex}>{index + 1}</span>
<Field
name={`answers.${index}`}
onFocus={() => {
switch (index) {
case 0:
return setFocused('a1');
case 1:
return setFocused('a2');
case 2:
return setFocused('a3');
case 3:
return setFocused('a4');
default:
return setFocused('');
}
}}
onBlur={() => {
switch (index) {
case 0:
return setFocused('');
case 1:
return setFocused('');
case 2:
return setFocused('');
case 3:
return setFocused('');
default:
return setFocused('');
}
}}
style={
focused === 'a1' && index === 0
? focusStyle
: focused === 'a2' && index === 1
? focusStyle
: focused === 'a3' && index === 2
? focusStyle
: focused === 'a4' && index === 3
? focusStyle
: quizFormInputText
}
/>
{values.answers.length >= 3 ? (
<i
onClick={() => arrayHelpers.remove(index)}
style={removeIcon}
>
{ioRemoveCircleSharp}
</i>
) : (
''
)}
</div>
{/* <div style={quizFormErrMsg}>
<ErrorMessage name={`answers.${index}`} />
</div> */}
{errors.answers &&
touched.answers &&
errors.answers !==
'Duplicate answers are not allowed' ? (
<div style={quizFormErrMsg}>
<ErrorMessage name={`answers.${index}`} />
</div>
) : null}
</div>
))
: null}
{values.answers.length <= 3 ? (
<div
// style={addHover ? moreAnswerIconHover : moreAnswerIcon}
style={moreAnswerIcon}
onClick={() => arrayHelpers.push('')}
// onMouseEnter={() => setAddHover(true)}
// onMouseLeave={() => setAddHover(false)}
>
Add
</div>
) : null}
</div>
)}
/>
<div style={labelInputContainer}>
<label htmlFor='correctAnswer' style={label}>
Correct Answer
</label>
<Field
min='1'
max={values.answers.length}
type='number'
name='correctAnswer'
onFocus={() => {
setFocused('ca');
}}
onBlur={() => {
setFocused('');
}}
style={focused === 'ca' ? focusStyle : quizFormInputText}
/>
{errors.correctAnswer && touched.correctAnswer ? (
<div style={quizFormErrMsg}>
Only 1 ~ {values.answers.length} are allowed.
</div>
) : null}
</div>
<div style={labelInputContainer}>
<label style={label}>Category</label>
<Field
as='select'
name='category'
onFocus={() => {
setFocused('category');
}}
onBlur={() => {
setFocused('');
}}
style={focused === 'category' ? focusStyle : quizFormInputText}
>
<option value='' disabled>
Select a category
</option>
<option value='Workout'>Workout</option>
<option value='Muscle'>Muscle</option>
<option value='Nutrition'>Nutrition</option>
<option value='Other'>Other</option>
</Field>
{errors.category && touched.category ? (
<div style={quizFormErrMsg}>{errors.category}</div>
) : null}
</div>
<button
// disabled={!dirty && isValid}
type='submit'
style={submitBtnHover ? submitButtonHover : submitButton}
onMouseEnter={() => setSubmitBtnHover(true)}
onMouseLeave={() => setSubmitBtnHover(false)}
>
Submit
</button>
</Form>
)}
</Formik>
</div>
);
};
// ========== Styles =========
const labelInputContainer = {
background: '',
margin: '20px 0',
};
const label = {
fontSize: '1.5rem',
margin: '10px 0 5px',
fontFamily: "'Cormorant Garamond', serif",
};
const quizFormInputText = {
display: 'block',
width: '100%',
flexDirection: 'column',
border: 'none',
borderBottom: '1px solid rgb(200, 200, 200)',
fontSize: '1.2rem',
padding: '10px 15px',
marginTop: '5px',
transition: '.3s',
};
const focusStyle = {
display: 'block',
width: '100%',
flexDirection: 'column',
border: 'none',
fontSize: '1.2rem',
padding: '10px 15px',
outline: 'none',
borderBottom: '1px solid #005bbb',
marginTop: '5px',
background: '#ecf5ff',
transition: '.3s',
};
const quizFormErrMsg = {
color: 'red',
fontSize: '.9rem',
};
// This includes an answer and error
const answerContainer = {
display: 'flex',
flexDirection: 'column',
};
// This includes an answer index, input, and a remove icon .
const indexAnswerIconContainer = {
marginTop: '20px',
display: 'flex',
alignItems: 'center',
gap: '10px',
position: 'relative',
};
const answerIndex = {
position: 'absolute',
top: '-4px',
left: '-4px',
fontSize: '1rem',
};
const removeIcon = {
position: 'absolute',
top: '0px',
right: '-8px',
fontSize: '1.2rem',
color: 'red',
cursor: 'pointer',
height: '25px',
};
const moreAnswerIcon = {
fontSize: '1.2rem',
fontFamily: "'Cormorant Garamond', serif",
color: '#005bbb',
padding: '5px 10px',
marginTop: "20px",
cursor: 'pointer',
transition: '.3s',
textAlign: 'center',
};
// const moreAnswerIconHover = {
// fontSize: '1.2rem',
// fontFamily: "'Cormorant Garamond', serif",
// color: '#005bbb',
// padding: '5px 10px',
// marginTop: "20px",
// cursor: 'pointer',
// transition: '.3s',
// textAlign: 'center',
// opacity: ".7"
// };
const submitButton = {
padding: '10px 18px',
marginTop: '20px',
borderRadius: '5px',
border: 'none',
width: '100%',
cursor: 'pointer',
transition: '.3s',
background: 'none',
color: '#005bbb',
fontSize: '1.5rem',
fontFamily: "'Cormorant Garamond', serif",
};
const submitButtonHover = {
padding: '10px 18px',
marginTop: '20px',
borderRadius: '5px',
border: 'none',
width: '100%',
cursor: 'pointer',
transition: '.3s',
background: '#ecf5ff',
color: '#005bbb',
fontSize: '1.5rem',
fontFamily: "'Cormorant Garamond', serif",
};
export default QuizEdit;
I have a question about how to set initial values into a form created by Formik and Yup. I wanna let users edit their quiz on an update page, so I wanna insert values of a quiz that a user updates into each input.
I can get a quiz object as q and checked by logging on to the console, and I got an error that cannot read the question property.
import { useParams } from 'react-router-dom';
import { collection, query, setDoc, addDoc, getDoc, doc } from 'firebase/firestore';
import db from '../config/firebase';
import { useState, useEffect } from 'react';
import { Formik, Form, Field, FieldArray, ErrorMessage } from 'formik';
import * as Yup from 'yup';
import { ioRemoveCircleSharp } from '../icons/icons';
Yup.addMethod(Yup.array, 'unique', function (message, mapper = a => a) {
return this.test('unique', message, function (list) {
return list.length === new Set(list.map(mapper)).size;
});
});
const quizSchema = Yup.object().shape({
question: Yup.string()
.min(10, 'Too Short!')
.max(250, 'Too Long!')
.required('Required'),
answers: Yup.array()
.of(Yup.string().max(50, 'Too Long!').required('Required'))
.unique('Duplicate answers are not allowed')
.min(2, `Minimum of 2 answers`),
correctAnswer: Yup.number('only numbers are allowed')
.min(1, 'type more than 1 please')
.max(4, 'type less than 5')
.required('Required'),
category: Yup.string().required('Required'),
createdAt: Yup.date(),
likes: Yup.number(),
});
const QuizEdit = () => {
const { id } = useParams();
const [focused, setFocused] = useState(false);
const [submitBtnHover, setSubmitBtnHover] = useState(false);
const [q, setQuiz] = useState()
useEffect(() => {
const getData = async () => {
const snap = await getDoc(doc(db, 'quizzes', id));
console.log(snap.data().answers);
const quiz = snap.data();
console.log(quiz);
setQuiz(quiz)
};
getData();
}, []);
console.log(`q => ${q}`)
return (
<div className='formikNewQuiz'>
<Formik
initialValues={{
question: q["question"],
answers: ['', ''],
correctAnswer: '',
category: '',
createdAt: new Date(),
likes: 0,
}}
validateOnChange
validationSchema={quizSchema}
onSubmit={async (values, { resetForm }) => {
// same shape as initial values
console.log(values);
const quizCollectionRef = collection(db, 'quizzes');
const payload = values;
await addDoc(quizCollectionRef, payload);
// values["question"] = "";
// values["answers"] = ["", ""];
// values.correctAnswer = ""
// values.category = ""
resetForm();
}}
>
{({ errors, touched, values, q }) => (
<Form>
<div style={labelInputContainer}>
<label htmlFor='question' style={label}>
Question
</label>
<Field
name='question'
onFocus={() => {
setFocused('question');
}}
onBlur={() => {
setFocused('');
}}
style={focused === 'question' ? focusStyle : quizFormInputText}
/>
{errors.question && touched.question ? (
<div style={quizFormErrMsg}>{errors.question}</div>
) : null}
</div>
<FieldArray
name='answers'
style={quizFormInputText}
render={arrayHelpers => (
<div style={labelInputContainer}>
<label htmlFor='answers1' style={label}>
Answers
</label>
{errors.answers &&
touched.answers &&
errors.answers === 'Duplicate answers are not allowed' ? (
<div style={quizFormErrMsg}>{errors.answers}</div>
) : null}
{values.answers && values.answers.length > 0
? values.answers.map((answer, index) => (
<div key={index} style={answerContainer}>
<div style={indexAnswerIconContainer}>
<span style={answerIndex}>{index + 1}</span>
<Field
name={`answers.${index}`}
onFocus={() => {
switch (index) {
case 0:
return setFocused('a1');
case 1:
return setFocused('a2');
case 2:
return setFocused('a3');
case 3:
return setFocused('a4');
default:
return setFocused('');
}
}}
onBlur={() => {
switch (index) {
case 0:
return setFocused('');
case 1:
return setFocused('');
case 2:
return setFocused('');
case 3:
return setFocused('');
default:
return setFocused('');
}
}}
style={
focused === 'a1' && index === 0
? focusStyle
: focused === 'a2' && index === 1
? focusStyle
: focused === 'a3' && index === 2
? focusStyle
: focused === 'a4' && index === 3
? focusStyle
: quizFormInputText
}
/>
{values.answers.length >= 3 ? (
<i
onClick={() => arrayHelpers.remove(index)}
style={removeIcon}
>
{ioRemoveCircleSharp}
</i>
) : (
''
)}
</div>
{/* <div style={quizFormErrMsg}>
<ErrorMessage name={`answers.${index}`} />
</div> */}
{errors.answers &&
touched.answers &&
errors.answers !==
'Duplicate answers are not allowed' ? (
<div style={quizFormErrMsg}>
<ErrorMessage name={`answers.${index}`} />
</div>
) : null}
</div>
))
: null}
{values.answers.length <= 3 ? (
<div
// style={addHover ? moreAnswerIconHover : moreAnswerIcon}
style={moreAnswerIcon}
onClick={() => arrayHelpers.push('')}
// onMouseEnter={() => setAddHover(true)}
// onMouseLeave={() => setAddHover(false)}
>
Add
</div>
) : null}
</div>
)}
/>
<div style={labelInputContainer}>
<label htmlFor='correctAnswer' style={label}>
Correct Answer
</label>
<Field
min='1'
max={values.answers.length}
type='number'
name='correctAnswer'
onFocus={() => {
setFocused('ca');
}}
onBlur={() => {
setFocused('');
}}
style={focused === 'ca' ? focusStyle : quizFormInputText}
/>
{errors.correctAnswer && touched.correctAnswer ? (
<div style={quizFormErrMsg}>
Only 1 ~ {values.answers.length} are allowed.
</div>
) : null}
</div>
<div style={labelInputContainer}>
<label style={label}>Category</label>
<Field
as='select'
name='category'
onFocus={() => {
setFocused('category');
}}
onBlur={() => {
setFocused('');
}}
style={focused === 'category' ? focusStyle : quizFormInputText}
>
<option value='' disabled>
Select a category
</option>
<option value='Workout'>Workout</option>
<option value='Muscle'>Muscle</option>
<option value='Nutrition'>Nutrition</option>
<option value='Other'>Other</option>
</Field>
{errors.category && touched.category ? (
<div style={quizFormErrMsg}>{errors.category}</div>
) : null}
</div>
<button
// disabled={!dirty && isValid}
type='submit'
style={submitBtnHover ? submitButtonHover : submitButton}
onMouseEnter={() => setSubmitBtnHover(true)}
onMouseLeave={() => setSubmitBtnHover(false)}
>
Submit
</button>
</Form>
)}
</Formik>
</div>
);
};
// ========== Styles =========
const labelInputContainer = {
background: '',
margin: '20px 0',
};
const label = {
fontSize: '1.5rem',
margin: '10px 0 5px',
fontFamily: "'Cormorant Garamond', serif",
};
const quizFormInputText = {
display: 'block',
width: '100%',
flexDirection: 'column',
border: 'none',
borderBottom: '1px solid rgb(200, 200, 200)',
fontSize: '1.2rem',
padding: '10px 15px',
marginTop: '5px',
transition: '.3s',
};
const focusStyle = {
display: 'block',
width: '100%',
flexDirection: 'column',
border: 'none',
fontSize: '1.2rem',
padding: '10px 15px',
outline: 'none',
borderBottom: '1px solid #005bbb',
marginTop: '5px',
background: '#ecf5ff',
transition: '.3s',
};
const quizFormErrMsg = {
color: 'red',
fontSize: '.9rem',
};
// This includes an answer and error
const answerContainer = {
display: 'flex',
flexDirection: 'column',
};
// This includes an answer index, input, and a remove icon .
const indexAnswerIconContainer = {
marginTop: '20px',
display: 'flex',
alignItems: 'center',
gap: '10px',
position: 'relative',
};
const answerIndex = {
position: 'absolute',
top: '-4px',
left: '-4px',
fontSize: '1rem',
};
const removeIcon = {
position: 'absolute',
top: '0px',
right: '-8px',
fontSize: '1.2rem',
color: 'red',
cursor: 'pointer',
height: '25px',
};
const moreAnswerIcon = {
fontSize: '1.2rem',
fontFamily: "'Cormorant Garamond', serif",
color: '#005bbb',
padding: '5px 10px',
marginTop: "20px",
cursor: 'pointer',
transition: '.3s',
textAlign: 'center',
};
// const moreAnswerIconHover = {
// fontSize: '1.2rem',
// fontFamily: "'Cormorant Garamond', serif",
// color: '#005bbb',
// padding: '5px 10px',
// marginTop: "20px",
// cursor: 'pointer',
// transition: '.3s',
// textAlign: 'center',
// opacity: ".7"
// };
const submitButton = {
padding: '10px 18px',
marginTop: '20px',
borderRadius: '5px',
border: 'none',
width: '100%',
cursor: 'pointer',
transition: '.3s',
background: 'none',
color: '#005bbb',
fontSize: '1.5rem',
fontFamily: "'Cormorant Garamond', serif",
};
const submitButtonHover = {
padding: '10px 18px',
marginTop: '20px',
borderRadius: '5px',
border: 'none',
width: '100%',
cursor: 'pointer',
transition: '.3s',
background: '#ecf5ff',
color: '#005bbb',
fontSize: '1.5rem',
fontFamily: "'Cormorant Garamond', serif",
};
export default QuizEdit;
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
第一次加载页面时,状态值
q
为undefined
,因为您在创建这样的状态时没有设置初始值。要解决此问题,您必须执行以下任一操作:在状态定义期间传入初始值,如下所示
,或者在渲染
Formik
组件之前检查q
是否可用,如下所示The state value
q
isundefined
the first time the page loads because you're not setting an initial value when you created the state like thisTo resolve this issue you have to either pass in the initial value during the state definition like this
OR check that
q
is available before rendering theFormik
component like this