React + Formik: Use value for nested object
TL;DR
Change in your Form.Control
the prop name
to name.en
/name.fr
First of all, initialValues
is a prop that will be set and won't change unless you pass the prop enableReinitialize
. So it isn't good to do this.state.project || { name: { 'en': '' }
because it will only assume the first value of that, it can be this.state.project
or { name: { 'en': '' }
, but you will never know.
Second, to solve your problem, if you look at the docs about 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.
But in your Form.Control
you are passing the name
attribute as name="name"
.
So it's trying to update name
and not e.g. name.en
.
You should change
<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}
/>
To
<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}
/>
Here is the docs that shows why you should use name.en
instead of just name
.
Comments
-
mrks almost 2 years
I have the following model for my React (TypeScript) app:
interface IProjectInput { id?: string; name: string | i18n; description: string | i18n; }
export interface i18n { [key: string]: string; }
I am using
Formik
andreact-bootstrap
to create a newProjectInput
from aForm
: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>
So in the end it should look like:
{ "name": { "en": "yes", "fr": "oui" }, "description" : "test", ... }
My problem is, that the value for the
name
input stays empty.I tried to add
const init = this.state.project || { name: { 'en': '' },
in myrender
or for mystate
, but this did not do anything.