javascript - React + Formik: ใช้ค่าสำหรับอ็อบเจ็กต์ที่ซ้อนกัน
ฉันมีโมเดลดังต่อไปนี้สำหรับแอป React (TypeScript) ของฉัน:
interface IProjectInput {
id?: string;
name: string | i18n;
description: string | i18n;
}
export interface i18n {
[key: string]: string;
}
ฉันกำลังใช้ Formik และ react-bootstrap เพื่อสร้าง ProjectInput ใหม่จาก Form:
import { i18n as I18n, ... } from 'my-models';
interface State {
validated: boolean;
project: IProjectInput;
}
/**
* A Form that can can edit a project
*/
class ProjectForm extends Component<Props, State> {
constructor(props: any) {
super(props);
this.state = {
project: props.project || {
name: {},
description: ''
},
validated: false
};
}
async handleSubmit(values: FormikValues, actions: FormikHelpers<IProjectInput>) {
let project = new ProjectInput();
project = { ...project, ...values };
console.log("form values", values);
// actions.setSubmitting(true);
// try {
// await this.props.onSubmit(project);
// } catch (e) { }
// actions.setSubmitting(false);
}
render() {
const { t } = this.props;
const getCurrentLng = () => i18n.language || window.localStorage.i18nextLng || '';
const init = this.state.project || {
name: {},
description: ''
};
return (
<div>
<Formik
// validationSchema={ProjectInputSchema}
enableReinitialize={false}
onSubmit={(values, actions) => this.handleSubmit(values, actions)}
initialValues={init}
>
{({
handleSubmit,
handleChange,
handleBlur,
values,
touched,
errors,
isSubmitting,
setFieldTouched
}) => {
return (
<div className="project-form">
<Form noValidate onSubmit={handleSubmit}>
<Form.Row>
<Form.Group as={Col} md={{span: 5}} controlId="projectName">
<Form.Label>
{t('projectName')}
</Form.Label>
// Input for ENGLISH text
<Form.Control
type="text"
name="name"
value={(values['name'] as I18n).en}
onChange={handleChange}
/>
// Input for FRENCH text
<Form.Control
type="text"
name="name"
value={(values['name'] as I18n).fr}
onChange={handleChange}
/>
</Form.Group>
ดังนั้นในที่สุดมันควรจะดูเหมือน:
{
"name": {
"en": "yes",
"fr": "oui"
},
"description" : "test",
...
}
ปัญหาของฉันคือ ค่าของช่องป้อนข้อมูล name ยังคงว่างอยู่
ฉันลองเพิ่ม const init=this.state.project || { name: { 'en': '' }, ใน render ของฉันหรือสำหรับ state ของฉัน แต่สิ่งนี้ไม่ทำอะไรเลย
คำตอบ #1
สรุปย่อ
เปลี่ยนใน Form.Control ของคุณ prop name เป็น name.en/name.fr
ก่อนอื่น, initialValues เป็น prop ที่ถูกกำหนดค่าและจะไม่เปลี่ยนแปลงเว้นแต่คุณจะส่ง prop enableReinitialize ดังนั้นจึงไม่ดีที่จะทำ this.state.project || { name: { 'en': '' } เพราะมันจะถือเพียงค่าครั้งแรกของนั้น ซึ่งอาจเป็น this.state.project หรือ { name: { 'en': '' } แต่คุณจะไม่มีวันรู้
ประการที่สอง ในการแก้ปัญหาของคุณ หากคุณดูเอกสารเกี่ยวกับ handleChange:
ตัวจัดการเหตุการณ์การเปลี่ยนแปลงข้อมูลทั่วไป นี่จะอัปเดต
values[key]โดยที่ key คือแอตทริบิวต์nameของอินพุตที่ส่งเหตุการณ์ หากแอตทริบิวต์nameไม่ปรากฏอยู่handleChangeจะมองหาแอตทริบิวต์idของอินพุต หมายเหตุ: "input" ที่นี่หมายถึงอินพุต HTML ทั้งหมด
แต่ใน Form.Control ของคุณ คุณกำลังส่งแอตทริบิวต์ name เป็น name="name"
ดังนั้นมันจึงพยายามที่จะอัปเดต name ไม่ใช่เช่น name.en
คุณควรเปลี่ยน
<Form.Control
type="text"
name="name"
value={(values['name'] as I18n).en}
onChange={handleChange}
/>
// Input for FRENCH text
<Form.Control
type="text"
name="name"
value={(values['name'] as I18n).fr}
onChange={handleChange}
/>
ถึง
<Form.Control
type="text"
name="name.en" // correct name
value={(values['name'] as I18n).en}
onChange={handleChange}
/>
// Input for FRENCH text
<Form.Control
type="text"
name="name.fr" // correct name
value={(values['name'] as I18n).fr}
onChange={handleChange}
/>
นี่คือเอกสารที่แสดงให้เห็นว่าทำไมคุณควรใช้ name.en แทนที่จะใช้แค่ name
คำตอบ #2
มันถูกอธิบายในเอกสาร https://formik.org/docs/guides/arrays
import React from 'react';
import { Formik, Form, Field } from 'formik';
export const NestedExample = () => (
<div>
<h1>Social Profiles</h1>
<Formik
initialValues={{
social: {
facebook: '',
twitter: '',
},
}}
onSubmit={values => {
// same shape as initial values
console.log(values);
}}
>
<Form>
<Field name="social.facebook" />
<Field name="social.twitter" />
<button type="submit">Submit</button>
</Form>
</Formik>
</div>
);
