React-Hook-Form Controller ignoring useEffect - reactjs

I have two components, where selectBirthMonth depends on SelectBirthYear. Which is why I use useEffect, to watch the selectedYear change, so that I can change selectedMonth in case it is needed.
code sandbox
So in controller context my components look the following
<Controller
control={control}
name="selectedBirthYear"
defaultValue={years[0]}
render={({ field }) => (
<SelectBirthYear
field={field}
years={years}
value={selectedYear}
defaultValue={selectedYear}
onChange={useEffect(() => {setSelectedYear(field.value)})}
/>
)}
/>
</div>
and ...
<Controller
control={control}
name="selectedBirthMonth"
defaultValue={months[0]}
render={({ field }) => (
<SelectBirthMonth
field={field}
startYear={startYear}
selectedYear={selectedYear}
months={months}
value={selectedMonth}
defaultValue={selectedMonth}
reducedMonths={reducedMonths}
onChange={useEffect(() => setSelectedMonth(field.value))}
/>
)}
/>
SelectBirthMonth totally ignored the following code though:
const [selectedMonth, setSelectedMonth] = useState(months[0]);
const watchYearChange = () => {
if(Number(selectedYear.name) == startYear){
setSelectedMonth(reducedMonths[reducedMonths.length - 1]);
}
};
useEffect(() => watchYearChange(), [selectedYear]);
Meaning, no matter, which year is chosen, the month won't react. What do I miss?

I would recommend using a small date library like date-fns to handle date related things. It's a great package, here are the docs for it. Also it can handle i18n for you if this should be a requirement in the future.
I used it in the below CodeSandbox and also corrected a few things:
when you use RHF you don't need to setup extra state for your component as RHF will manage the form state for you.
it's much simpler if you just use one date for the whole birthdate - this way you will always have the current values for year, month and day and don't need to watch values or use extra defined state

The answer is way too easy, to be working, but it does. Several times I've been reading this post here How to change React-Hook-Form defaultValue with useEffect()?, but could't really understand, where and how do I use setValue. As I assumed, the value of my select just wasn't changing, even though I was watching the sibling state change.
So I put the setValue into the useEffect hook and the rest stayed the same:
const monthSelect = (data) => {
setSelectedMonth(months[data.id - 1]);
};
const watchYearChange = () => {
if(Number(selectedYear.name) == startYear){
setSelectedMonth(lastAvaliableMonth)
}
};
useEffect(() => setValue('selectedBirthMonth', lastAvaliableMonth), [selectedYear]);
Here are two siblings just in case:
<Controller
control={control}
name="selectedBirthYear"
defaultValue={years[0]}
render={({ field }) => (
<SelectBirthYear
field={field}
years={years}
value={selectedYear}
defaultValue={selectedYear}
onChange={useEffect(() => {setSelectedYear(field.value)})}
/>
)}
/>
... and
<Controller
control={control}
name="selectedBirthMonth"
defaultValue={selectedMonth}
render={({ field }) => (
< SelectBirthMonth
field={field}
startYear={startYear}
selectedYear={selectedYear}
months={months}
value={selectedMonth}
reducedMonths={reducedMonths}
onChange={useEffect(() => monthSelect(field.value)), [selectedMonth]}/>
)}
/>
If this solution somehow is not good, please let me know in the comments. I am a total beginner.

Related

react date picker multi range with react hook form

The reason we use react hook form is that it decreases our state count and increases performance. But I didn't know how to do it when using Date range for one datepicker.
How to keep two data in one controller?
`() => {
const [startDate, setStartDate] = useState(new Date());
const [endDate, setEndDate] = useState(null);
const onChange = (dates) => {
const [start, end] = dates;
setStartDate(start);
setEndDate(end);
};
return (
<DatePicker
selected={startDate}
onChange={onChange}
startDate={startDate}
endDate={endDate}
selectsRange
inline
/>
);
};`
If this piece of code is my code, I can only capture one value with selected, but I need to return 2 values. How can I use this in the best way with the react hook form?
<Controller
name="orderDate"
control={control}
render={({ field }) => (
<DatePicker
selected={field.value}
onChange={(date) => field.onChange(date)}
selectsRange
/>
)}
/>
I've got the exact same issue last week, here is the solution...
You just need to pass (at least) the value to the "Controller function", you achieve that using only the field object.
In any case, When the DatePicker component is used for a date range, it will require a two-position vector data type, where the start and end dates are stored as a string. But if you try to manipulate and control this component via the onChange function that comes from the reack-hook-form it's gonna screw up the entire thing, because is made for a one-to-one data flux. Then the processes are done separately but the vector is still sent to the controller. Here is the code!
const [dateRange, setDateRange] = useState([null, null]);
const [startDate, endDate] = dateRange;
<Controller
//is a prop that we get back from the useForm Hook and pass into the input.
control={control}
//is how React Hook Form tracks the value of an input internally.
name={name}
//render is the most important prop; we pass a render function here.
render={({
//The function has three keys: field , fieldState, and formState.
field, // The field object exports two things (among others): value and onChange
}) => (
<>
<DatePicker
selectsRange={true}
startDate={startDate}
endDate={endDate}
onChange={(e) => {
setDateRange(e);
field.onChange(e);
}}
isClearable={true}
className="form-control"
/>
</>
)}
rules={{
required: `The ${label} field is required`,
}}
/>

Creatable Select from react-select/creatable with API integration in react-final-form

I have successfully integrated the creatable with react-final-form but the problem arises when i try to do an API call. I am trying to do an API call with onIputChange but whenever i get a response(in props), my drop down closes. Its closing up on receiving the new options. I tried it with normal select and it works fine. Here is my example code.
<Field
name="tags"
component={ ({
input,
...rest
}) => (<CreatableSelect {...input}
{...rest}}
/>)}
isMulti
options={sortedTagsOptions}
onInputChange={(x) => handleChangeTypeheadInput(x)}/>
The API call works fine. Only one issue occurs, the dropdown closes and goes out of focus as well as the written value goes away in the select. If anyone has any idea on how to deal with this problem please let me know. This works fine without Field component though. Maybe mutate the form values? but having problems in that regard as well.
Solved it by creating a separate component rather than doing it all in one component.
const AppCreateableSelectForm = ( {
input: {
name, onChange, value,
},
className,
incomingOptions,
isClearable,
isMulti,
onFocus,
closeMenuOnSelect,
incomingStyle,
isDisabled,
placeholder,
onInputChange,
noOptionsMessage,
}) => {
return <CreatableSelect
name={name}
isMulti={isMulti}
onFocus={onFocus}
closeMenuOnSelect={closeMenuOnSelect}
options={incomingOptions}
isClearable={isClearable}
placeholder={placeholder ? placeholder:`Select`}
className={className}
isDisabled={isDisabled}
styles={incomingStyle ? incomingStyle : style}
value={value}
onChange={onChange}
onInputChange={onInputChange}
noOptionsMessage={noOptionsMessage}
theme={theme => ({
...theme,
colors: {
...theme.colors,
primary: `#637282`,
},
})}
components={{
IndicatorSeparator: () => null,
}}
/>
Some props might not be used in this code since i copied only relative code.

react-hook-form Controller with react-draft-wysiwyg

I need some help. I was using react-final-form in my project but I've decided to change for react-hook-form. By the way, I love it, but I got stuck. :/
On my page, I'm using an editor to collect some info from a user. At the moment I'm using react-draft-wysiwyg.
Here the code:
parentComponent.js
/**
* react-hook-form library
*/
const {
register,
handleSubmit,
control
} = useForm({
mode: 'onChange'
});
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
as={<WYSIWYGEditor />}
name='editor'
control={control}
onChange={([ event ]) => event.target.value}
/>
</form>
WYSIWYGEditor.js
const WYSIWYGEditor = ({ input }) => {
const [ editorState, setEditorState ] = useState(EditorState.createEmpty());
const onEditorStateChange = editorState => {
setEditorState(editorState);
};
return (
<React.Fragment>
<div className={classes.root}>
<Editor
editorState={editorState}
wrapperClassName='wrapper-class'
editorClassName='editor-class'
onEditorStateChange={onEditorStateChange}
/>
</div>
</React.Fragment>
);
};
export default WYSIWYGEditor;
PLEASE NOTE:
The input prop comes from the coding with react-final-form. The input was passing the characters I was typing. So, if I leave input as props it fails because it doesn't exist. React-hook-form doesn't pass an input.
I've changed that with props:
const WYSIWYGEditor = props=> {
console.log(props)
and I get the following in the console.log when I type anything:
{name: "editor", value: undefined, onChange: ƒ}
As you can see, value is undefined. How can I structure the Controller in order to pass a value each time I type something in the editor?
Thanks for your help
I found a solution.
value is undefined because obviously on component load there is nothin' to load. If you don't want to see undefined just pass defaultValue='' from the controller:
<Controller
as={<WYSIWYGEditor />}
name='editor'
control={control}
onChange={([ event ]) => event.target.value}
defaultValue='' <== here
/>
Now, the issue that doesn't allow to return any typed value, is because I have declared the onChange from the controller. So, the right code would be:
<Controller
as={<WYSIWYGEditor />}
name='editor'
control={control}
defaultValue=''
/>
Also, in the WYSIWYGEditor.js file, you need to replace what before was input with props. The reason is that they are passing exactly the same Object, which contains a value, onChange, and onBlur function.
Here a code sandbox with the working code:
https://codesandbox.io/s/trusting-mountain-k24ys?file=/src/App.js

Issue with Semantic ui react calendar

I've defined the following component to be rendered using JSX:
const TestingDate = () => {
return (
<Container>
<DateInput
clearable
clearIcon={<Icon name="remove" color="red" />}
name="date"
value="2 Apr 2020"
onChange={a => handleDateChange(a)}
/>
</Container>
);
};
However, the issue I'm having is that, in the handleDataChange, I have to keep track of the date (namely the value prop of DateInput which is imported from "semantic-ui-calendar-react", but I can't find any reasonable way of passing that to the handleDateChange function... I can see it's a super basic issue but I'm kind of stuck since it's my first time working with DateInput and the tutorial used the older style where you'd bind a callback to the DateInput as a prop.
If it helps, what I'd like to do is just call this line setDate(value) in the handleDataChange function. Thanks!
I was having the same issue and was able to resolve it by doing the following.
const TestingDate = () => {
const [date, setDate] = useState(null);
function handleDateChange(name, value) {
setDate(value)
}
return (
<Container>
<DateInput
clearable
clearIcon={<Icon name="remove" color="red" />}
name="date"
value="2 Apr 2020"
onChange={(a, {name, value}) => handleDateChange(name, value)}
/>
</Container>
);
};
I hope that helps!

How to use custom Input with Formik in React?

I'm trying to use DatePicker within Formik. But when I click DatePicker's date its form value is not changed. Instead, I got this error:
Uncaught TypeError: e.persist is not a function
at Formik._this.handleChange (formik.es6.js:5960)
I shortify code, the code is below
const SomeComponent = () => (
<Formik
render={({
values,
handleSubmit,
handleChange,
setFieldValue
}) => {
return (
<div>
<form onSubmit={handleSubmit}>
<DatePicker
name={'joinedAt'}
value={values['joinedAt']}
onChange={handleChange}
/>
</form>
</div>
)
}}
/>
)
I googled few documents, https://github.com/jaredpalmer/formik/issues/187 and https://github.com/jaredpalmer/formik/issues/86
So I tried like below, but it's not working.
...setFieldValue
<DatePicker
name={'joinedAt'}
value={values['joinedAt']}
onChange={setFieldValue}
/>
I resolve this like
<DatePicker
name={'joinedAt'}
value={values['joinedAt']}
onChange={e => setFieldValue('joinedAt', e)}
/>
Update on 2020-03-08:
You can use e.target.value on setFieldValue's second prop, depends on your custom input design. Thanks to Brandon.
If you have deeper nesting, you should use Formik Field.
Example:
<Formik
validationSchema={schema}
initialValues={initialValues}
onSubmit={(values, actions) => {}}
>
<Field name="colors" component={ColorsEditor} colors={colorArray}/>
</Formik>
const ColorsEditor = ({ field, colors, form, ...props }) => {
return (
<div>
<Button onClick={() => form.setFieldValue('colors', "myValue")}>
</Button>
</div>
)
}
So the Field component already include the form, where live the setFieldValue that you can use where you need. It also include the errors and needed fields for additional customization.
For html primitive input fields handleChange works like a charm, but for custom components, you've to use setFieldValue to change the value imperatively.
onChange={e => setFieldValue('joinedAt', e)}
The accepted answer didn't work for me and didn't show the selected date. It has been almost a year so there might be some changes with the implementation. Though this can be fix by using toDateString() like this
<DatePicker
name={'joinedAt'}
value={values['joinedAt']}
onChange={e => setFieldValue('joinedAt', e.toDateString())}
/>
The Formik solution for this is the useField() method. You may need to create a wrapper for components like DatePicker that you may not have source access to.
The documentation provides an example:
const MyTextField = ({ label, ...props }) => {
const [field, meta, helpers] = useField(props);
return (
<>
<label>
{label}
<input {...field} {...props} />
</label>
{meta.touched && meta.error ? (
<div className="error">{meta.error}</div>
) : null}
</>
);
};
In short, the method takes in the custom props which describe the field. Then you can expand the assigned field value and props in your custom field.
https://github.com/jaredpalmer/formik/issues/692
you need to set value manually if you are using setFieldValue method

Resources