React Hook Form Controller - reactjs

I am using react hook form controller on material UI textField component where the type i am giving as file.
<Controller
name={name}
control={control}
render={({ field: { value, onChange } }) => (
<ReactHookFormSelector
selector={selector}
type={type}
multiline={multiline}
value={value}
onChange={(event) => {
console.log(event.target.files)
onChange(event.target.files)
}
}
/>
Here i want to store this file and value in the redux store state while submitting this form
And i want to use that file and value later in another Textfield(input=file) component.
Can anybody help me how i can store this file and value in redux and use it later?

If you use react-hooks-form, you can use setValue to the file .
and then use watch or getValues() functions from the react-hooks-form to access the state in another component.
onChange={(event) => {
console.log(event.target.files)
setValue("files",event.target.files[0].name)
}
}
then use watch :
const filesWatch = getValues("files")
More in here : https://react-hook-form.com/api/useform

Related

Passing value to hidden input from dropdown menu in react

I have react-select dropdown menu and hidden input which I pass to form when submiting...
using useState hook I created variable which tracks changes to react-select dropdown menu.
Hidden input has this variable as value also. I thought this would be enough.
But when I submit the form, console. log shows me that value of input is empty despite that variable that was selected from dropdown menu is actually updated.
I mean variable that I have chosen console logs some value, but hidden input thinks that it is still empty.
Does it means I have to rerender manually page each time I change that variable so input gets it's new value using useEffect ? Which is bad solution for me, I don't like it, thought it would be done automatically.
Or instead of useState I must create and use variable via Redux ? Which I also don't like, use redux for such small thing fills overcomplicated.
Isn't there any nice elegant solution ? :)
import { useForm } from 'react-hook-form';
const [someVar,setSomeVar]=useState('');
const {
register,
handleSubmit,
formState: { errors },
} = useForm({ mode: 'onBlur' });
const handleFormSubmit = (data) => {
console.error('success');
};
const handleErrors = (errors) => {
console.error(errors);
console.log(document.getElementsByName('hiddenInput')[0].value);
};
const options = {
hiddenInput: {
required: t('hiddenInput is required'),
},
};
.......
<form onSubmit={handleSubmit(handleFormSubmit, handleErrors)}>
<Select
options='...some options list'
onChange={(value) => setSomeVar(value)}
/>
<input
name='hiddenInput'
value={someVar}
{...register('hiddenInput', options.hiddenInput)}
/>
<button>submit</button>
</form>
UPDATED
Its because getElementsByName returns an array of elements.
You probably want
document.getElementsByName('hiddenInput')[0].value
I should add that really you should use a ref attached to the input and not access it via the base DOM API.
const hiddenRef = useRef(null)
// ...
cosnt handleSubmit =(e)=>{
console.log(hiddenRef.current.value);
}
// ...
<input
name='hiddenInput'
value={someVar}
ref={hiddenRef}
/>
However as you are using react-hook-form you need to be interacting with its state store so the library knows the value.
const {
register,
handleSubmit,
formState: { errors },
setValue
} = useForm({ mode: 'onBlur' });
// ...
<form onSubmit={handleSubmit(handleFormSubmit, handleErrors)}>
<Select
options='...some options list'
onChange={(value) => setValue('hiddenInput', value)}
/>
<input
name='hiddenInput'
{...register('hiddenInput', options.hiddenInput)}
/>
<button>submit</button>
</form>
You can remove const [someVar,setSomeVar]=useState('');
However, this hidden input is not really necessary as you mention in comments. You just need to bind the dropdown to react hook form.
// controller is imported from react hook form
<form onSubmit={handleSubmit(handleFormSubmit, handleErrors)}>
<Controller
control={control} // THIS IS FROM useForm return
name="yourDropdown"
rules={{required: true}}
render={({
field: { onChange, value, name, ref }
}) => (
<Select
options={options}
inputRef={ref}
value={options.find(c => c.value === value)}
onChange={val => onChange(val.value)}
/>
)}
/>
<button>submit</button>
</form>

Function components cannot be given refs. Attempts to access this ref will fail in select component

Here I defined a select component, and I wanted to display it if a condition is true. This select field appears when one of the values ​​of the previous select input is selected. But here is when the new select field (i.e. my select component), and I choose a value from this select, this value is not submitted by my form, and is not present in my data when I do a console log after submitting my form, but the name of the value field is there, but not the value. And i have a warning in my console stating:
Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
Check the render method of Controller
My Select Component
export const SelectCompanyField = () => {
// const [page, setPage] = useState(1);
// const [perPage, setPerPage] = useState(10);
const { data, error } = useSWR<ServerResponse, any>(companyUrls.base, url => getDataByParams(companyUrls.base));
console.log("Data", data, "Error", error);
console.log(data?.entities);
return (
<Select
showSearch
placeholder="Choisir une compagnie"
optionFilterProp="children"
filterOption={(input, option: any) =>
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
>
{data?.entities.map(d => (
<option value={d.index} key={d.id} >
{d.name}
</option>
))}
</Select>
);
};
The select component to display if the condition is true
<Col md={24} lg={24} sm={24}>
{firstOptionValue &&
<div className="ant-form-item">
<label className="label">Compagnie <span className="text-danger">*</span></label>
<Controller
as={<SelectCompanyField />}
name="company"
control={control}
defaultValue={""}
rules={{ required: false }}
{errors.company && "Company is required"}
</div>
}
</Col>
The console log of my data submit
{
"firstName": "Atom",
"lastName": "Proton",
"username": "xyz#test.ml",
"password": "00789",
"phoneNumber": "44258569",
"profile": "ADMIN",
"userType": "B2B_CLIENT",
"company": ""
}
The company part with the empty quotes, is the part where it should have the value of the field I chose in my select component.
I would just like the selected value of the field or the option, appear in my data so that I can submit my form.
Thanks
SelectCompanyField needs to be:
export const SelectCompanyField = React.forwardRef(() => {
...
});
When rendering using Controller's as prop, it passes a ref prop to the as prop. React see's a ref being passed, but you aren't using a forwardRef component.
In addition to this, then you need to actually use the ref (and props) which you don't appear to be doing now in SelectCompanyField that are being provided by Controller
The docs will help you out
☝️ Please read the docs, but your SelectCompanyField receives props like this:
export const SelectCompanyField = React.forwardRef(({field,meta}) => {
const { onChange, onBlur, value, ref } = field
return <Select onChange={onChange} value={value} .../>
});
It Is your job to add the onChange handler and the value to the Select component you are rendering. I don't know what Select component it is - is it frame a component framework? is it just a regular select? - so I can't tell you how to do it, but the hook form docs are pretty clear.
For anyone with this problem and a component that you can't use ref (because you haven't created the component can't change it, or it doesn't need a ref prop, or you are using typescript with a generic component and used a different/custom name for the ref prop), you can use the react-hook-mask Controller render prop (https://stackoverflow.com/a/69671594/4850646), instead of as, which allows you to customize which props are passed:
Instead of:
<Controller as={<SelectCompanyField />} ... />
You can use:
<Controller render={({ field: { ref, ...field } }) => <SelectCompanyField {...field} />} ... />

React - issue with initialValues ant design when using referenced component

Using ant design input and forms, I am having trouble using intialValues when a component is referenced in. In this case, I am trying to pass in CustomInput.js, a ant design Input, into my form as <Component/>. Everything works except the initialValues.
Any ideas what to do to make intialValues work?
https://codesandbox.io/s/initialvalues-forked-keftp?file=/index.js:382-414
You have to pass the props from your custom component down to Input, specifically the value prop:
function CustomInput(
{ field, value = "", onChange = (e) => {}, disabled, ...rest },
ref
) {
return (
<Input
ref={ref}
value={value}
onChange={onChange}
disabled={disabled}
{...rest}
/>
);
}
DEMO
Note: I'm unsure as to what your field prop is. Since it's not a supported prop as part of the component's API, I left it out.

MUI's Autocomplete AS MULTIPLE input + react-hook-form + controlling default values not working (TypeError: Can't read property 'filter' of undefined)

I am trying to use Material-UI Autocomplete as multiple input with react-hook-form and control the defaultValues of the Autocomplete dynamically (render the component prefilled, when editing data, based on already saved data fetched from a database).
So the main question is:
What is the best way to control the default values in an Material-UI Autocomplete component and use it with react-hook-form?
What i did so far:
With using functions and hooks in react, I have wrapped an
Autocomplete component in React Hook Form's Controller to control
the state. I tried to implemented the solutions from the docs of MUI
and react-hook-form and the solutions of the below threads.
I created a minimal Sandbox here
What it does
When i set the defaultValue in Controller, it works to display the controlled default value, but throws me an error: TypeError: Cannot read property 'filter' of undefined
<Controller
as={
<Autocomplete
multiple
value={defaultValues}
onChange={(e, values) => setValue("food", values)}
...
renderInput={params => ( ... )}
/>
}
control={control}
name="food"
defaultValue={defaultValues} // <- this makes the error
/>
When i don't set the defaultValue in Controller, it works perfectly to being able to select multiple values like expected, but it doesn't show the default value.
What is so confusing is, that the Autocomplete has to be controlled with value/onChange and also the Controller has to control with defaultValue/setValue, and it seems they conflict in my case.
It works better when setting defaultValue={ [] } and using a
useEffect and controlling the default value only with
setValue("food", defaultOption);
So i created another Sandbox here
Thanks to Bill's answer i refactored the code to a renderProp
like proposed in the docs:
Yet another sandbox here
Now it works like a charm, but i had to set the onChange prop of
the Autocomplete like this:
onChange={(e, values) => setValue("food", values)}
instead of what the docs proposed to do: (using the passed onChange)
onChange={e => props.onChange(e.something)}
It works, but is this the right way to combine Autocomplete and
react-hook-form?
Compare the questions with these threads:
The main difference to the other threads i am trying to do, is to set the defaultValues of a multiple Autocomplete.
Proper way to use react-hook-form Controller with Material-UI Autocomplete
MUI Autocomplete's 'defaultValue' not working when used with Controller of react-hook-form
Why is initial value not set in Material UI Autocomplete using react-hook-form?
The advised solution in the docs of react-hook-form:
https://react-hook-form.com/api/#Controller
And the Code from Material UI docs:
https://material-ui.com/components/autocomplete/#multiple-values
I was able to get this working by doing the following:
<Controller
name='test'
control={control}
render={({onChange, ...props}) => (
<AutoComplete
{...props}
data-testid='test-select'
width={350}
label='Auto Complete'
onChange={onChange}
options={eventTypes}
getOptionLabel={(option) => option ? option.name : ''}
renderOption={(option) => option.name }
getOptionSelected={(option) => option.name}
renderInput={(params) => (
<TextField {...params} error={error} helperText={helperText} label={label} placeholder={label} />
)}
onChange={(e, data) => onChange(data)}
{...props}
/>
)}
/>
However, I've not found a way to validate this using react-hook-form
You could try custom validation (in case if you are using MUI's Autocomplete with multiple={true}):
<Controller
...
rules={{
validate: (data) => {
if(data.length === 0) return false;
}
}}
/>
Now it works like a charm, but i had to set the onChange prop of the
Autocomplete like this:
onChange={(e, values) => setValue("food", values)}
You can do onChange={(e, newValue) => props.onChange(newValue)}

Material-UI - TextField - select text programmatically

Material-UI V1 beta.
Could not find the answer in the Docs.
How do I select text of a TextField component?
Create a ref to it, then call the value of the ref. Something like this:
<TextField ref="myTextField" />
// Call this in the component that contains the text field so 'this' is set properly
function getTextFieldValue() {
return this.refs.myTextField.getValue();
}
This is known as an uncontrolled react component. An alternative would be to use a controlled component and save the value in your state. Here is some info on the difference between controlled and uncontrolled components: https://reactjs.org/docs/uncontrolled-components.html
if you are using a stateless functional component then you can use react hooks.
Also make sure you are using inputRef
import React, { useState, useRef } from "react";
let MyFunctional = props => {
let textInput = useRef(null);
return (
<div>
<Button
onClick={() => {
setTimeout(() => {
console.log(textInput.current.value);
}, 100);
}}
>
Focus TextField
</Button>
<TextField
fullWidth
required
inputRef={textInput}
name="firstName"
type="text"
placeholder="Enter Your First Name"
label="First Name"
/>
</div>
);
};

Resources