How to use react-hook-form with my customized react-date-picker? - javascript

I am trying to use react-hook-form with my customized date-picker, but I only see this example (Is it possible to use react-datepicker with react hooks forms?), which is using the default react-date-picker.
However, it just only works on the original react-date-picker.
I tried the same way with my customized date-picker, and it doesn't work...
This is my customized date-picker:
import React, { useState } from 'react';
import ReactDatePicker from 'react-datepicker';
import tw from "date-fns/locale/zh-TW";
import "react-datepicker/dist/react-datepicker.css";
const DatePicker = props => {
const [date, setDate] = useState('');
return (
<ReactDatePicker
className="form-control"
selected={date}
onChange={date => setDate(date)}
locale={tw}
dateFormat="yyyy/MM/dd"
dateFormatCalendar="yyyy年 MM月"
isClearable
/>
)
};
export default DatePicker;
Here is how I use react-hook-form with my customized date-picker:
import React from 'react';
import { useForm, Controller } from 'react-hook-form';
import DatePicker from '../../components/UI/Form/DatePicker';
const Form = props => {
const { register, handleSubmit, control} = useForm();
const onSubmit = (data) => {
console.log(data);
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>First Name:</label>
<input type="text" name='firstName' ref={register} />
<label>Last Name:</label>
<input type='text' name='lastName' ref={register} />
<label>birthday:</label>
<Controller as={DatePicker} control={control} valueName="selected" name="birthday" />
<input type="submit" value="Submit" />
</form>
);
}
export default Form;
After I submit the form, the 'birthday' value is undefined.
Do I still need to add any props to my customized date-picker?
{
birthday: undefined,
firstName: "Mike",
lastName: "Smith"
}

The customised form control currently does not offer any props to control it from outside the component. For someone actually using the component, it has to have both selected and onChange fields to extract value out of it (The react-date-picker has these props and hence works)
Controller by default has an onChange which reads from an event passed to it, which is why you see it omitted from examples like this:
<Controller as={TextField} name="TextField" control={control} defaultValue="" />
To change your custom component to work with Controlller syntax, you can expose the selected and onChange accordingly:
const DatePicker = ({ selected, onChange }) => {
return (
<ReactDatePicker
className="form-control"
selected={selected}
onChange={onChange}
locale={tw}
dateFormat="yyyy/MM/dd"
dateFormatCalendar="yyyy年 MM月"
isClearable
/>
)
};
and on the Controller:
<Controller
as={DatePicker}
control={control}
valueName="selected"
name="birthday"
onChange={(date) => date};
/>

Also it's better if you pass onChangeName="onChangeDates" instead so you can pass value in.
<ControllerWrapper
as={
<DatePicker
error={has(formErrors, fieldsConfiguration.datePicker.name)}
/>
}
rules={fieldsConfiguration.datePicker.rules}
name={fieldsConfiguration.datePicker.name}
onChangeName="onChangeDates"
onChange={dateRangePickerSelector}
/>

Related

React-hook-form form key value returns undefined after filling inputs and submitting

I'm learning react and I'm having some difficulties. At the moment my question is related to a Login page using ReactJS, Typescript and styled-components. Where I only have the email and password part. I'm manipulating the form with the react hook form, but I have a problem. When I run a console.log of the values ​​of my email and password keys, it just returns undefined. And no error is pointed out in the console.
enter image description here
Here is my code.
import React from "react";
import { useForm, SubmitHandler } from "react-hook-form";
import { Form, TitleSecondary } from "./Form.style";
import { Button } from "../Button/Button";
import { InputContainer, InputLabel, InputField } from "./Form.style";
interface IForm {
email: string;
password: string;
}
export const FormLogin = React.forwardRef<HTMLInputElement>(( props, ref) => {
const { register, handleSubmit } = useForm<IForm>();
const onSubmit: SubmitHandler<IForm> = (data) => console.log(data);
return (
<Form onSubmit={handleSubmit(onSubmit)}>
<TitleSecondary>Login</TitleSecondary>
<InputContainer>
<InputLabel htmlFor="email">Email</InputLabel>
<InputField
{...register("email")}
id="email"
type="email"
placeholder=""
ref={ref}
required
/>
</InputContainer>
<InputContainer>
<InputLabel htmlFor="password">Password</InputLabel>
<InputField
{...register("password")}
id="password"
type="password"
placeholder="mail#example.com"
ref={ref}
required
/>
</InputContainer>
<Button />
</Form>
)
})
I confess that I have tried several things since then, but none have been successful.

how to set the value of Input from state ( React app)

hope you're doing well!
I am looking to set the value of Input from the state that i receive from props! I could set all the information from "profile" state in the placeholder but when putting it in the value field it doesn't show anything
here's my code and the form i use :
<Form
name="basic"
wrapperCol={{ span: 24 }}
onFinish={onUpdate}
onFinishFailed={onFinishFailed}>
<FormItem>
<Input prefix={<ContactsTwoTone />} placeholder={profile.name} />
</FormItem>
<FormItem name="email"
rules={[
{
type: 'email',
message: 'The input is not valid E-mail!',
}
]}
>
<Input value={profile.email} name="name" prefix={<MailTwoTone />} placeholder={profile} />
</FormItem>
<FormItem name="mobile" value={profile.mobile} >
<Input value={profile.mobile} name="mobile" prefix={<PhoneTwoTone />} placeholder={profile.mobile} />
</FormItem>
<FormItem name="addres">
<Input name="addres" prefix={<HomeTwoTone />} placeholder={profile.addres} />
</FormItem>
<FormItem name="description">
<Input.TextArea name="description" placeholder="description" rows={4} prefix={<ContainerTwoTone />} />
</FormItem>
<FormItem>
<Button className="width-100" type="primary" htmlType="submit" onClick={onUpdate} >Update</Button>
</FormItem>
</Form> ```
the useEffect function and the declaration of state :
const [visible, setVisible] = useState(false);
const FormItem = Form.Item;
const [profile, setProfile] = useState({});
useEffect(() => {
setProfile(props.profile);
},[props.profile] );
const showDrawer = () => {
setVisible(true);
};
I am looking to set the value of Input from the state that i receive
from props!
If you're just looking to set the input from state, here is a simple example of that.
Solution and Demo
https://codesandbox.io/s/trusting-swanson-1q0ftq
import { useState } from "react";
const initialValue = "John";
const App = () => {
const [name, setName] = useState(initialValue);
console.log("render");
console.log("state: ", name);
const handleChange = (event) => {
const value = event.target.value;
setName(value);
};
return (
<div>
<h2>Form</h2>
<pre>Type in the input...</pre>
<form>
<input type="text" onChange={handleChange} value={name} />
</form>
<pre>state: {name}</pre>
</div>
);
};
export default App;
Regarding the rest of your question and the example code, it looks like you may be thinking that props should inform some new state based on your useEffect.
However, useEffect runs every time the prop is updated, and when the prop is updated React already triggers a re-render. We can use the new value in our component already, so we don't need to mess with useEffect or state. This would be redundant and unnecessary.
You seem to essentially be asking about how to pass props into your form. Below is an example with editable profile section that is used to manage the profile state. The form section simply reads the values from state and displays the profile information. This illustrates the desired behavior you're describing. The form is just a component that receives the profile state passed down as props.
The profile fields rely on state for their values. The form component relies on props based on that state.
Example Passing Props to Component
https://codesandbox.io/s/damp-fire-sbxmoz
import { useState } from "react";
const initialProfile = {
firstName: "John",
lastName: "Doe"
};
const Form = ({ firstName, lastName }) => (
<div>
<h2>Form</h2>
<pre>Values based on profile...</pre>
<form>
<input
label="First Name"
type="text"
name="firstName"
value={firstName}
/>
<input label="Last Name" type="text" name="lastName" value={lastName} />
</form>
</div>
);
const App = () => {
const [profileFields, setProfileFields] = useState(initialProfile);
const { firstName, lastName } = profileFields;
console.log("render, profile fields: ", profileFields);
const handleChange = (event) => {
const { name, value } = event.target;
setProfileFields(() => {
return {
...profileFields,
[name]: value
};
});
};
return (
<div>
<Form firstName={firstName} lastName={lastName} />
<h2>Profile</h2>
<pre>Type to edit profile fields...</pre>
<form>
<input
label="First Name"
type="text"
onChange={handleChange}
name="firstName"
value={firstName}
/>
<input
label="Last Name"
type="text"
onChange={handleChange}
name="lastName"
value={lastName}
/>
</form>
</div>
);
};
export default App;
Explanation
You can type in the profile fields to see how the form values are updated via props. Typing in the top form won't update values because these are read-only and "set from props".
You might not want a read-only form, but this is just based on your example. You can pass your profile data as props to whatever component needs it.
I think the idea I'm hoping to communicate here is to think about a "single source of truth" for your profile data (your state) and decide where you want that to live versus what other components need to access it.
Hopefully that helps.
Try to log them out and see if they are doing what they are supposed to.
const SomeInput = (props) => {
const { name, placeholder, value } = props;
console.log(`${name} placeholder: ${placeholder} value: ${value}`);
return(
<input placeholder={placeholder} value={value}/>
);
};
const App = () => {
const [state,setState] = React.useState('foo');
console.log(`App state: ${state}`);
return(
<div>
<SomeInput name={'input1'} placeholder={state}/>
<SomeInput name={'input2'} value={state}/>
</div>
);
};
ReactDOM.render(<App/>,document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>

Is there a way to get a React component's internal values when I click a button on the parent?

Suppose I have a component like this -
const MyForm = ({ formId }) => (
<div>
<input type="text" placeholder="Full name"></input>
<input type="text" placeholder="Email"></input>
</div>
)
export default MyForm;
And then I have my App.js like so -
import React from "react";
import MyForm from "./MyForm";
const App = () => (
<div id="app">
<MyForm formId="formOne"></MyForm>
<MyForm formId="formTwo"></MyForm>
<button onClick={
() => {
// Here, when the user clicks the button,
// I want to get values of both the textboxes,
// from both the component instances
}
}>Submit</button>
</div>
)
export default App;
So basically, what I want is - when the button is clicked, I want to be able to retrieve the values of the textboxes. One way to do this is to raise an event from inside MyForm.js so that every text change is bubbled up to the parent via a callback function prop, but that feels too cumbersome, especially if the form has a lot of fields. Is there any simple or direct way to do this? Do I need to involve global state management tools like Redux?
State inside a component is specific only to that component, the parent , children or sibling of a component have no idea of the state. The only way to communicate the value from one component to another component is via props . In your case, what we need is a state to reside at the App which can then be passed as a prop to both the MyForm Components.
App.js
const [ formState, setFormState ] = useState({ formOne: {fullName: '', Email: ''}, formTwo: '' })
const updateFormValues = (formId, key, value) => {
const stateCopy = JSON.parse(JSON.stringify(formState));
const formToUpdate = stateCopy[formId];
formToUpdate[key] = value;
setFormState(stateCopy)
}
<MyForm formId="formOne" values={formState.formOne} updateFormValues={updateFormValues}></MyForm>
<MyForm formId="formTwo" values={formState.formTwo} updateFormValues={updateFormValues}></MyForm>
MyForm.js
const MyForm = ({ formId, values, updateFormValues }) => {
const onInputChange = (e, key) => {
updateFormValues(formId, key, e.target.value)
}
return(
<div>
<input type="text" onChange={(e) => onInputChange(e, 'fullName'} value={values.fullName} placeholder="Full name"></input>
<input type="text" onChange={(e) => onInputChange(e, 'email'} value={values.email} placeholder="Email"></input>
</div>
)}
export default MyForm;
To have access to data inside children components you need to lift the state to the parent component.
One-way data flow
Identify every component that renders something based on that state.
Find a common owner component (a single component above all the components that need the state in the hierarchy).
Either the common owner or another component higher up in the hierarchy should own the state.
If you can’t find a component where it makes sense to own the state, create a new component solely for holding the state and add it somewhere in the hierarchy above the common owner component.
One way to do this:
import React, { useState } from "react";
function MyForm(props) {
const { handleChange, values } = props;
return (
<div>
<label htmlFor="name">Your name</label>
<input
type="text"
placeholder="Full name"
onChange={handleChange}
value={values.name}
id="name"
name="name"
/>
<label htmlFor="email">Your email</label>
<input
type="email"
placeholder="Email"
onChange={handleChange}
value={values.email}
id="email"
name="email"
/>
</div>
);
}
function App() {
const [values, setValues] = useState({ name: "", email: "" });
const handleChange = (event) => {
const updatedForm = { ...values, [event.target.name]: event.target.value };
setValues(updatedForm);
};
return (
<div id="app">
<MyForm
formId="formOne"
values={values}
handleChange={handleChange}
></MyForm>
<button
onClick={() => {
console.log(values);
}}
>
Submit
</button>
</div>
);
}
export default App;

React Hooks Form : undefined values on submit

I took the example from the documentation :
import React from "react";
import { useForm } from "react-hook-form";
export default function App() {
const { register, handleSubmit, watch, formState: { errors } } = useForm();
const onSubmit = data => console.log(data);
console.log(watch("example"));
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input defaultValue="test" {...register("example")} />
<input type="submit" />
</form>
);
}
But on every change or on submit, I got undefined for each field
I tried to install the library again but nothing change and I got undefined everywhere...seems to be a problem with the register function. Does anybody got the same issue ?
With v7 the usage of register changed as noted in the comments. If you still need to use v6, you have to write it like this:
function App() {
const { register, handleSubmit, watch, formState: { errors } } = useForm();
const onSubmit = data => console.log(data);
console.log(watch("example"));
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input defaultValue="test" name="example" ref={register} />
<input type="submit" />
</form>
);
}
Docs v6
In my case it was a typo:
<input defaultValue="test" {...(register('name'), { required: true })} />
// submit => { name: undefined }
Instead of:
<input defaultValue="test" {...(register('name', { required: true }))} />
// submit => { name: "test" }
Hopefully it can help someone else.
In my case, I was using a Controller, so to fix the Undefined value I just had to pass defaultValues to useForm.
See the rules section here: https://react-hook-form.com/api/useform/watch
const { register, handleSubmit, control, setValue} = useForm<MyFormValues>({
defaultValues : {
receiveUpdates: false
}
});
<Controller
control={control}
name="receiveUpdates"
render={({ field }) => (
<FormControlLabel
control={
<Checkbox
ref={field.ref}
checked={field.value}
onChange={field.onChange}
/>
}
label="Receive Email Updates?"
labelPlacement="start"
/>
)}
/>
I had this issue when using the Input component from reactstrap. Using that component made all my values undefined. I switch the Input to a normal input and was able to read in values
Before:
<Input
placeholder="Password"
type="password"
id="password"
defaultValue=""
{...register('password')}
required
/>
Fixed:
<input
placeholder="Password"
type="password"
id="password"
defaultValue=""
{...register('password')}
required
/>
In my case I installed like "npm i react-hook-form" and I don't know why, but it was installed ^6.15.8 version, and I removed it and try again and then it was install correctly. So try to check out your version of react-hook-form

React Final Form Error: Must specify either a render prop

I am trying to build a simple form with React-Final-Form like this:
import * as React from "react";
import {
PrimaryButton,
} from "office-ui-fabric-react/lib/Button";
import { Form , Field } from "react-final-form";
import { FORM_ERROR } from "final-form";
import { IUserFormValues } from "../../models/user";
import { RootStoreContext } from "../../stores/rootStore";
import TextInputNew from "./TextInputNew";
const NewUIForm = () => {
const rootStore = React.useContext(RootStoreContext);
const { login } = rootStore.userStore;
return (
<Form
onSubmit={(values: IUserFormValues) =>
login(values).catch((error) => ({
[FORM_ERROR]: error,
}))
}
render={({
handleSubmit,
}) => (
<Form onSubmit={handleSubmit}>
<Field name="email" component={TextInputNew} />
<Field name="email" component={TextInputNew} />
<PrimaryButton type='submit' text="Save" />
</Form>
)}
/>
);
};
export default NewUIForm;
The TextInputNew Component is this:
import * as React from "react";
import { TextField } from "office-ui-fabric-react/lib/TextField";
import { FieldRenderProps } from "react-final-form";
interface IProps extends FieldRenderProps<string, HTMLInputElement> {}
const TextInputNew: React.FC<IProps> = ({ input }) => {
return (
<div>
<input {...input} />
<TextField label="Standard" />
</div>
);
};
export default TextInputNew;
Then I got this error when I use this NewUIForm component
Error: Must specify either a render prop, a render function as children, or a component prop to ReactFinalForm
By the way, the UI framework is Fluent-UI
Can anyone help me? Thanks!!
You're second <Form> should be <form>.
<form onSubmit={handleSubmit}>
<Field name="email" component={TextInputNew} />
<Field name="email" component={TextInputNew} />
<PrimaryButton type='submit' text="Save" />
</form>
To anyone else who might encounter this vague error message, the issue is something going wrong in the render function of <Form>.
For OP, it was using the wrong form tag inside of <Form>.
For me, it was a misspelled property on a <Field> component (components={MyComponent}, oops).
Since the error can be caused by any number of reasons and the message wasn't very specific, one can get an idea of where the problem might be via browser debugger, in my case this is what it looked like:

Categories

Resources