Formik React 在字段数组外使用 setFieldValue 设置值 onChange
Formik React set values onChange with setFieldValue outside Array of Fields
我的目标 是 select 一个 Student
和 onChange
动态 setFieldValues
对于 FieldArray
嵌套的 schedule
对象数组。
当我 select 学生:1 时,我应该为他们的 3 个计划项目中的每一个获取一个字段数组,学生 2:将有示例 4 项目。示例涉及诸如 older example with Fruit on CodeSandbox. A second CodeSandbox also related for this.
之类的内容
我的问题 是当我尝试 map
一个学生的 schedule
它显示 schedule.map
是一个 TypeError。因此不填充我的 FieldArray
要编辑的字段。
TypeError: values.schedule.map is not a function
我的JSON数据:
{
"count": 2,
"results": [
{
"id": "1",
"name": "Tom",
"schedule": [
{
"class": "GYM",
"time": "9:00,
},
{
"class": "History",
"time": "10:00",
},
{
"class": "Lunch",
"time": "11:00",
},
},
{
"id": "2",
"name": "Nancy",
"schedule": [
{
"class": "Math",
"time": "9:00,
},
{
"class": "Science",
"time": "10:00",
},
{
"class": "English",
"time": "11:00",
},
{
"class": "History",
"time": "12:00",
},
}
]
}
我的 Formik 组件:
const NewStudentSchedule = () => {
const [studentSchedule, setSchedule] = useState([]);
useEffect(() => {
const getSchedule = async () => {
try {
const { data } = await fetchContext.authAxios.get(
`/api/schedule/`
);
setSchedule(data.results);
// console.log(data);
} catch (err) {
console.log(err);
}
};
getSchedule();
}, [fetchContext]);
const initialValues = {
id: '',
schedule: [
{
class: '',
time: '',
},
],
};
return (
<Formik
initialValues={initialValues}
onSubmit={async (values, { resetForm }) => {
alert(JSON.stringify(values, null, 2));
resetForm();
}}
>
{({
isSubmitting,
values,
setFieldValue,
handleChange,
errors,
touched,
}) => (
<Form>
<div className="row">
<div className="col-md-6 mr-auto">
<div className="form-group">
<label htmlFor="status">STUDENT</label>
<Field
className="form-control"
as="select"
name="id"
onChange={(event) => {
handleChange(event);
const selectedTask = schedule.find(
(selectStudent) => selectStudent.id === event.target.value
);
setFieldValue(
`schedule.step`,
selectedTask.schedule.step
);
}}
>
<option value="" defaultValue disabled>
...
</option>
{schedule &&
schedule.map((i) => (
<option key={i.id} value={i.id}>
{i.name}
</option>
))}
</Field>
</div>
</div>
</div>
<div className="col-md-12">
<h3 className="pt-3">CREATE SCHEDULE</h3>
<FieldArray name="schedule">
{({ insert, remove, push }) => (
<>
{values.schedule.length > 0 &&
values.schedule.map((task, index) => (
<div className="row" key={index}>
<div className="col-11">
<div className="row">
<div className="col">
<div className="form-group">
<label
htmlFor={`schedule.${index}.class`}
>
CLASS
</label>
<Field
className="form-control"
name={`schedule.${index}.class`}
type="text"
/>
</div>
</div>
<div className="col">
<div className="form-group">
<label
htmlFor={`schedule.${index}.time`}
>
TIME
</label>
<Field
className="form-control"
name={`schedule.${index}.time`}
type="text"
/>
</div>
</div>
</div>
</div>
<div className="col-1">
<button
type="button"
className="btn delete"
onClick={() => remove(index)}
>
Delete
</button>
</div>
</div>
))}
<button
type="button"
className="btn add"
onClick={() => push({ class: '', time: '' })}
>
Add
</button>
</>
)}
</FieldArray>
<button
className="btn float-right mb-5"
type="submit"
disabled={isSubmitting}
>
SUBMIT
</button>
</div>
</Form>
)}
</Formik>
)
}
我的应用截图:
这是一个棘手的问题
您似乎想用从 API 获取的数据预填充表单。此外,您希望用户能够在学生(Tom 和 Nancy)之间切换。在这种情况下,您需要在 Formik 的 initialValues
中设置所有学生的数据。在处理 <FieldArray>
中的 class 值时,您需要确保您指的是特定学生的正确 class 。
您的代码存在一些问题,例如从未使用过 studentSchedule
并且 schedule
未定义。我继续做了一些假设,并在下面提出了这个解决方案。它通过在 <FieldArray>
中使用 两个 索引来引用正确的 class:selectedStudentIndex
和 taskIndex
const App = () => {
const [studentSchedule, setSchedule] = useState([]);
useEffect(() => {
const getSchedule = async () => {
try {
const { data } = await getData();
setSchedule(data.results);
// console.log(data);
} catch (err) {
console.log(err);
}
};
getSchedule();
}, []);
return <ScheduleForm students={studentSchedule} />;
};
const ScheduleForm = ({ students }) => {
const initialValues = {
id: "",
students
};
return (
<Formik
enableReinitialize={true}
initialValues={initialValues}
onSubmit={async (values, { resetForm }) => {
console.log(JSON.stringify(values, null, 2));
}}
>
{({
isSubmitting,
values,
setFieldValue,
handleChange,
errors,
touched
}) => {
const selectedStudentIndex = values.students.findIndex(
({ id }) => id === values.id
);
return (
<Form>
<div className="row">
<div className="col-md-6 mr-auto">
<div className="form-group">
<label htmlFor="status">STUDENT</label>
<Field
className="form-control"
as="select"
name="id"
onChange={(event) => {
handleChange(event);
const selectedTask = students.find(
(selectStudent) =>
selectStudent.id === event.target.value
);
setFieldValue(
`schedule.step`,
selectedTask.schedule.step
);
}}
>
<option value="" defaultValue disabled>
...
</option>
{values.students &&
values.students.map((i) => (
<option key={i.id} value={i.id}>
{i.name}
</option>
))}
</Field>
</div>
</div>
</div>
<div className="col-md-12">
<h3 className="pt-3">CREATE SCHEDULE</h3>
<FieldArray name={`students.${selectedStudentIndex}.schedule`}>
{({ insert, remove, push }) => (
<div>
{values.id !== "" &&
values.students[selectedStudentIndex].schedule.map(
(task, taskIndex) => (
<div className="row" key={taskIndex}>
<div className="col-11">
<div className="row">
<div className="col">
<div className="form-group">
<label
htmlFor={`students.${selectedStudentIndex}.schedule.${taskIndex}.class`}
>
CLASS
</label>
<Field
className="form-control"
name={`students.${selectedStudentIndex}.schedule.${taskIndex}.class`}
type="text"
/>
</div>
</div>
<div className="col">
<div className="form-group">
<label
htmlFor={`students.${selectedStudentIndex}.schedule.${taskIndex}.time`}
>
TIME
</label>
<Field
className="form-control"
name={`students.${selectedStudentIndex}.schedule.${taskIndex}.time`}
type="text"
/>
</div>
</div>
</div>
</div>
<div className="col-1">
<button
type="button"
className="btn delete"
onClick={() => remove(taskIndex)}
>
Delete
</button>
</div>
</div>
)
)}
<button
type="button"
className="btn add"
onClick={() => push({ class: "", time: "" })}
>
Add
</button>
</div>
)}
</FieldArray>
<button
className="btn float-right mb-5"
type="submit"
disabled={isSubmitting}
>
SUBMIT
</button>
</div>
</Form>
);
}}
</Formik>
);
};
如果有人想出更简单的方法,我会很感兴趣。
我的目标 是 select 一个 Student
和 onChange
动态 setFieldValues
对于 FieldArray
嵌套的 schedule
对象数组。
当我 select 学生:1 时,我应该为他们的 3 个计划项目中的每一个获取一个字段数组,学生 2:将有示例 4 项目。示例涉及诸如 older example with Fruit on CodeSandbox. A second CodeSandbox also related for this.
之类的内容我的问题 是当我尝试 map
一个学生的 schedule
它显示 schedule.map
是一个 TypeError。因此不填充我的 FieldArray
要编辑的字段。
TypeError: values.schedule.map is not a function
我的JSON数据:
{
"count": 2,
"results": [
{
"id": "1",
"name": "Tom",
"schedule": [
{
"class": "GYM",
"time": "9:00,
},
{
"class": "History",
"time": "10:00",
},
{
"class": "Lunch",
"time": "11:00",
},
},
{
"id": "2",
"name": "Nancy",
"schedule": [
{
"class": "Math",
"time": "9:00,
},
{
"class": "Science",
"time": "10:00",
},
{
"class": "English",
"time": "11:00",
},
{
"class": "History",
"time": "12:00",
},
}
]
}
我的 Formik 组件:
const NewStudentSchedule = () => {
const [studentSchedule, setSchedule] = useState([]);
useEffect(() => {
const getSchedule = async () => {
try {
const { data } = await fetchContext.authAxios.get(
`/api/schedule/`
);
setSchedule(data.results);
// console.log(data);
} catch (err) {
console.log(err);
}
};
getSchedule();
}, [fetchContext]);
const initialValues = {
id: '',
schedule: [
{
class: '',
time: '',
},
],
};
return (
<Formik
initialValues={initialValues}
onSubmit={async (values, { resetForm }) => {
alert(JSON.stringify(values, null, 2));
resetForm();
}}
>
{({
isSubmitting,
values,
setFieldValue,
handleChange,
errors,
touched,
}) => (
<Form>
<div className="row">
<div className="col-md-6 mr-auto">
<div className="form-group">
<label htmlFor="status">STUDENT</label>
<Field
className="form-control"
as="select"
name="id"
onChange={(event) => {
handleChange(event);
const selectedTask = schedule.find(
(selectStudent) => selectStudent.id === event.target.value
);
setFieldValue(
`schedule.step`,
selectedTask.schedule.step
);
}}
>
<option value="" defaultValue disabled>
...
</option>
{schedule &&
schedule.map((i) => (
<option key={i.id} value={i.id}>
{i.name}
</option>
))}
</Field>
</div>
</div>
</div>
<div className="col-md-12">
<h3 className="pt-3">CREATE SCHEDULE</h3>
<FieldArray name="schedule">
{({ insert, remove, push }) => (
<>
{values.schedule.length > 0 &&
values.schedule.map((task, index) => (
<div className="row" key={index}>
<div className="col-11">
<div className="row">
<div className="col">
<div className="form-group">
<label
htmlFor={`schedule.${index}.class`}
>
CLASS
</label>
<Field
className="form-control"
name={`schedule.${index}.class`}
type="text"
/>
</div>
</div>
<div className="col">
<div className="form-group">
<label
htmlFor={`schedule.${index}.time`}
>
TIME
</label>
<Field
className="form-control"
name={`schedule.${index}.time`}
type="text"
/>
</div>
</div>
</div>
</div>
<div className="col-1">
<button
type="button"
className="btn delete"
onClick={() => remove(index)}
>
Delete
</button>
</div>
</div>
))}
<button
type="button"
className="btn add"
onClick={() => push({ class: '', time: '' })}
>
Add
</button>
</>
)}
</FieldArray>
<button
className="btn float-right mb-5"
type="submit"
disabled={isSubmitting}
>
SUBMIT
</button>
</div>
</Form>
)}
</Formik>
)
}
我的应用截图:
这是一个棘手的问题
您似乎想用从 API 获取的数据预填充表单。此外,您希望用户能够在学生(Tom 和 Nancy)之间切换。在这种情况下,您需要在 Formik 的 initialValues
中设置所有学生的数据。在处理 <FieldArray>
中的 class 值时,您需要确保您指的是特定学生的正确 class 。
您的代码存在一些问题,例如从未使用过 studentSchedule
并且 schedule
未定义。我继续做了一些假设,并在下面提出了这个解决方案。它通过在 <FieldArray>
中使用 两个 索引来引用正确的 class:selectedStudentIndex
和 taskIndex
const App = () => {
const [studentSchedule, setSchedule] = useState([]);
useEffect(() => {
const getSchedule = async () => {
try {
const { data } = await getData();
setSchedule(data.results);
// console.log(data);
} catch (err) {
console.log(err);
}
};
getSchedule();
}, []);
return <ScheduleForm students={studentSchedule} />;
};
const ScheduleForm = ({ students }) => {
const initialValues = {
id: "",
students
};
return (
<Formik
enableReinitialize={true}
initialValues={initialValues}
onSubmit={async (values, { resetForm }) => {
console.log(JSON.stringify(values, null, 2));
}}
>
{({
isSubmitting,
values,
setFieldValue,
handleChange,
errors,
touched
}) => {
const selectedStudentIndex = values.students.findIndex(
({ id }) => id === values.id
);
return (
<Form>
<div className="row">
<div className="col-md-6 mr-auto">
<div className="form-group">
<label htmlFor="status">STUDENT</label>
<Field
className="form-control"
as="select"
name="id"
onChange={(event) => {
handleChange(event);
const selectedTask = students.find(
(selectStudent) =>
selectStudent.id === event.target.value
);
setFieldValue(
`schedule.step`,
selectedTask.schedule.step
);
}}
>
<option value="" defaultValue disabled>
...
</option>
{values.students &&
values.students.map((i) => (
<option key={i.id} value={i.id}>
{i.name}
</option>
))}
</Field>
</div>
</div>
</div>
<div className="col-md-12">
<h3 className="pt-3">CREATE SCHEDULE</h3>
<FieldArray name={`students.${selectedStudentIndex}.schedule`}>
{({ insert, remove, push }) => (
<div>
{values.id !== "" &&
values.students[selectedStudentIndex].schedule.map(
(task, taskIndex) => (
<div className="row" key={taskIndex}>
<div className="col-11">
<div className="row">
<div className="col">
<div className="form-group">
<label
htmlFor={`students.${selectedStudentIndex}.schedule.${taskIndex}.class`}
>
CLASS
</label>
<Field
className="form-control"
name={`students.${selectedStudentIndex}.schedule.${taskIndex}.class`}
type="text"
/>
</div>
</div>
<div className="col">
<div className="form-group">
<label
htmlFor={`students.${selectedStudentIndex}.schedule.${taskIndex}.time`}
>
TIME
</label>
<Field
className="form-control"
name={`students.${selectedStudentIndex}.schedule.${taskIndex}.time`}
type="text"
/>
</div>
</div>
</div>
</div>
<div className="col-1">
<button
type="button"
className="btn delete"
onClick={() => remove(taskIndex)}
>
Delete
</button>
</div>
</div>
)
)}
<button
type="button"
className="btn add"
onClick={() => push({ class: "", time: "" })}
>
Add
</button>
</div>
)}
</FieldArray>
<button
className="btn float-right mb-5"
type="submit"
disabled={isSubmitting}
>
SUBMIT
</button>
</div>
</Form>
);
}}
</Formik>
);
};
如果有人想出更简单的方法,我会很感兴趣。