React + Formik:为嵌套对象使用值
React + Formik: Use value for nested object
我的 React (TypeScript) 应用程序有以下模型:
interface IProjectInput {
id?: string;
name: string | i18n;
description: string | i18n;
}
export interface i18n {
[key: string]: string;
}
我正在使用 Formik
和 react-bootstrap
从 Form
:
创建一个新的 ProjectInput
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
输入的值保持为空。
我试图在我的 render
或 state
中添加 const init = this.state.project || { name: { 'en': '' },
,但这没有做任何事情。
TL;DR
将您的 Form.Control
道具 name
更改为 name.en
/name.fr
首先,initialValues
是一个会被设置的道具,除非你通过道具enableReinitialize
,否则不会改变。所以做 this.state.project || { name: { 'en': '' }
是不好的,因为它只会假设第一个值,它可以是 this.state.project
或 { name: { 'en': '' }
,但你永远不会知道。
其次,要解决您的问题,请查看有关 handleChange
:
的文档
General input change event handler. This will update the values[key]
where key is the event-emitting input's name
attribute. If the name
attribute is not present, handleChange
will look for an input's id
attribute. Note: "input" here means all HTML inputs.
但是在您的 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}
/>
这里的 docs 说明了为什么您应该使用 name.en
而不是 name
.
我的 React (TypeScript) 应用程序有以下模型:
interface IProjectInput {
id?: string;
name: string | i18n;
description: string | i18n;
}
export interface i18n {
[key: string]: string;
}
我正在使用 Formik
和 react-bootstrap
从 Form
:
ProjectInput
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
输入的值保持为空。
我试图在我的 render
或 state
中添加 const init = this.state.project || { name: { 'en': '' },
,但这没有做任何事情。
TL;DR
将您的 Form.Control
道具 name
更改为 name.en
/name.fr
首先,initialValues
是一个会被设置的道具,除非你通过道具enableReinitialize
,否则不会改变。所以做 this.state.project || { name: { 'en': '' }
是不好的,因为它只会假设第一个值,它可以是 this.state.project
或 { name: { 'en': '' }
,但你永远不会知道。
其次,要解决您的问题,请查看有关 handleChange
:
General input change event handler. This will update the
values[key]
where key is the event-emitting input'sname
attribute. If thename
attribute is not present,handleChange
will look for an input'sid
attribute. Note: "input" here means all HTML inputs.
但是在您的 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}
/>
这里的 docs 说明了为什么您应该使用 name.en
而不是 name
.