I am using reactjs controlled input
reference for below code
<input type="text"
value={this.state.username}
onChange={(e) => this.setState({ username: e.target.value })}
/>
I want to avoid extra code like value , onChange And move them to component like
<input type="text" setValueOnChange=(this,'username')/>
While getting exactly the result you want is not possible, just because it is not a syntactically valid JSX. It is possible to make something looking quite similar to that:
// in render
<input type="text"
value={this.state.username}
onChange={setValue(this, 'username')}
/>
// add this method to the component
setValue(propName) {
return (e) => {
this.setState({ [propName]: e.target.value })
}
}
You can actually extract the setValue function and reuse it with any other component:
// in render
<input type="text"
value={this.state.username}
onChange={setValue(this, 'username')}
/>
// somewhere outside of the component
function setValue(component, propName) {
return (e) => {
component.setState({ [propName]: e.target.value })
}
}
I believe I've seen some quite popular React forms handling library which was doing exactly this, but in a bit smarter way — they have cached the generated function to avoid creating a bunch of new function objects on each render. Just can't recall how was it named — there are too much new libs in the react world :)
Related
I am totally new to react, I have sign up form which have 5 five input fields.
I created one functional component for TextField
MyTextField.js
function MyTextField(props) {
return (
<TextField
label={props.label}
name={props.name}
placeholder={props.placeholder}
type={props.type}
onChange={props.handleOnChange}
value={props.value}
></TextField>
)
}
export default MyTextField
Signup.js
function SignUp() {
const [value, setValue] = useState('')
function FunConfirmPassword(event) {
setValue = event.target.value //this function is calling, if I comment this line
console.log('FunConfirmPassword called - password value = '+ value)
}
return (
<MyTextField type="text" label="Firstname" name="firstName" placeholder="Enter Firstname"></MyTextField>
<MyTextField type="text" label="Lastname" name="Lastname" placeholder="Enter Lastname"></MyTextField>
<MyTextField type="text" label="emailID" name="emailID" placeholder="Enter emailID"></MyTextField>
<MyTextField type="password" label="password" name="password" placeholder="Enter password"></MyTextField>
<MyTextField handleOnChange={FunConfirmPassword} type="password" label="confirmpassword" name="confirmpassword" placeholder="Enter confirmpassword"></MyTextField>
)
}
I am trying to learn how to set the value of TextField and get the value of same textfield.
At the same time, it must be re-usable function
how to set the value of TextField
In your code, the <TextField> gets its value from a prop:
value={props.value}
So to supply that value, pass it as that prop:
<MyTextField value={value} ...
and get the value of same textfield
That you're already doing here:
<MyTextField handleOnChange={FunConfirmPassword} ...
However, you have a mistake in your handler function here:
setValue = event.target.value
setValue is a function. This should be:
setValue(event.target.value);
Additionally, the console.log statement immediately following it won't print what you expect. React state updates are asynchronous and batched. Either log event.target.value instead, or use a useEffect to respond to the updated value value and log it there. (Or don't log it to the console at all, it's not really necessary.)
I've prepared a codepen example for you, where you can see how this works.
https://codepen.io/dns_nx/pen/BaxjdKd
Basically, you set a property to the child component, in which you define the function which is being executed, when called by the child (see property onChange).
To set the value in the MyTextField component, you just have to pass the current value of the variable in SignUp to your MyTextField component (see property value):
function SignUp(props) {
const [firstName, setFirstName] = useState('');
...
<MyTextField
type="text"
label="Firstname"
name="firstName"
placeholder="Enter Firstname"
value={firstName}
onChange={(event) => setFirstName(event.target.value}
/>
...
}
Then you need to call the function where you need it in your child component (i.e. onChange event) to pass the new value to the parent component (SignUp). The value is set by the property value:
function MyTextField(props) {
...
<TextField
...
value={props.value}
onChange={(event) => props.onChange(event)}
...
></TextField>
}
This is the way to pass values from child components to parent components.
But as larger your application gets, it makes it hard to handle. Therefore I would recommend you to use a state management library like Redux (react-redux).
https://www.npmjs.com/package/react-redux
and use this with the toolkit package
https://www.npmjs.com/package/#reduxjs/toolkit
Previously I used to write like this:
<input className="form-control" name="productImage" type='file' onChange={handleImageUpload} ref={register({ required: true })} />
After the update I have to write like this:
<input className="form-control" type="file" {...register('productImage', { required: true })} />
How do I use onChange={handleImageUpload} on the updated version of React Hook Form?
Here is the migration docs
Please pardon my mistakes in the manner of asking the question. I'm new to these things.
Thank you.
https://github.com/react-hook-form/react-hook-form/releases/tag/v7.16.0
V7.16.0 has introduced this new API for custom onChange.
<input
type="text"
{...register('test', {
onChange: (e) => {},
onBlur: (e) => {},
})}
/>
You just have to move the onChange props after {...register(...)}
const productImageField = register("productImage", { required: true });
return (
<input
className="form-control"
type="file"
{...productImageField}
onChange={(e) => {
productImageField.onChange(e);
handleImageUpload(e);
}}
/>
)
(Dec 3 2021) edit: this approach is no longer correct since react-hook-form v7.16.0's changes, see #Bill's answer.
In register documentation https://react-hook-form.com/api/useform/register, sample exists on Custom onChange, onBlur section :
// onChange got overwrite by register method
<input onChange={handleChange} {...register('test')} />
// register's onChange got overwrite by register method
<input {...register('test')} onChange={handleChange}/>
const firstName = register('firstName', { required: true })
<input
onChange={(e) => {
firstName.onChange(e); // method from hook form register
handleChange(e); // your method
}}
onBlur={firstName.onBlur}
ref={firstName.ref}
/>
So for your case :
const productImageRegister = register("productImage", {required: true})
<input className="form-control"
type="file"
{...productImageRegister }
onChange={e => {
productImageRegister.onChange(e);
handleImageUpload(e);
}} />
For me, decoration solution worked
const fieldRegister = register("productImage", {required: true})
const origOnChange = fieldRegister.onChange
fieldRegister.onChange = (e) => {
const res = origOnChange(e)
const value = e.target.value
// do something with value
return res
}
For field declaration use
<input {...fieldRegister}/>
Was stuck with the same problem. For me the problem was that my onChange was above the react-hook-form's {...register} and moving it below the register solved the problem for me!!
I faced a similar issue recently when migrating to V7. If it can help anybody.
A parent component handling the form was passing down to a wrapper the register function, the wrapper passing it down again to an input that needed debouncing on change.
I called the register formLibraryRef in case I wanted to use a different library later but overall I had to do something like:
const { onChange, ...rest } = formLibraryRef(inputName);
pass the onChange to the function that is itself passed to the native onChange event of the input:
const handleDebouncedChange: (event: React.ChangeEvent<HTMLInputElement>) => void = (
event: ChangeEvent<HTMLInputElement>,
) => {
onChange(event);
if (preCallback) {
preCallback();
}
debounceInput(event);
};
and then pass the rest to the input:
<input
aria-label={inputName}
name={inputName}
data-testid={dataTestId}
className={`form-control ...${classNames}`}
id={inputId}
onChange={handleDebouncedChange}
onFocus={onFocus}
placeholder={I18n.t(placeholder)}
{...rest}
/>
The register section in the docs here: https://react-hook-form.com/migrate-v6-to-v7/ gives a bit more info on how to get the onChange and shows an example for Missing ref.
You can use react-hook-form control
<Controller
render={({ field }) => <input onChange={event=>{
handleImageUpload(event);
field.onChange(event);
}} />}
name="image"
control={control}
/>
I have a React Bootstrap Form component
https://react-bootstrap.github.io/components/forms/#forms
I just need to be able to get the name/ref of the input box that the user is typing inside so I can update the state dynamically. I did this already with the standard Form using:
this.setState({
[event.target.name]: event.target.value
})
but I wanted to use React's Bootstrap form. I can already get the value of the Form input, but I can't find a way to get its reference name (like if the input box is for dealerName, I am unable to get the string 'dealerName' so I can update it dynamically instead of having to hardcode the state property name to a value to update it) so I can dynamically update the state. Without that, I would have to create multiple separate functions for all the different forms I have.
This is my sample React Bootstrap Form:
<Form>
<Form.Group controlId="formDealersAdd">
<Form.Label>userId:</Form.Label>
<Form.Control type="text" placeholder="userId" ref={this.userId} onChange={this.handleInputChange}/>
<Form.Label>dealerName:</Form.Label>
<Form.Control type="text" placeholder="dealerName" ref={this.dealerName} onChange={this.handleInputChange} />
<Form.Label>dealer id:</Form.Label>
<Form.Control type="text" placeholder={did} ref={this.did} onChange={this.handleInputChange} />
<Button variant="secondary" size="lg" onClick={this.handleDealersAddFormClick}>SUBMIT</Button>{' '}
</Form.Group>
</Form>
My constructor looks like this:
class App extends Component {
constructor(props) {
super(props);
this.userId= React.createRef();
this.dealerName = React.createRef();
this.did = React.createRef();
this.state = {
userId: '',
dealerName: '',
did: '',
};
}
}
And my handleInputChange function:
handleInputChange = (event) => {
event.preventDefault()
// manually hardcoding it like this causes issues and makes my code bad
this.setState({
userId: this.userId.current.value,
dealerName: this.dealerName.current.value,
did: this.did.current.value
})
}
I was originally handling the inputChange by simply setting the state with
this.setState({
[event.target.name]: event.target.value
})
and this worked fine for the standard Form (non-React Boostrap form) and it does return the correct value for the input that is being actively updated/being typed inside by a user, but event.target.name does not work.
Thus, as you can see above, I just manually hardcoded the values to be updated inside the state object, but this is messy and causes error when the user clicks to see a new Form on my website and the properties are null and the state tries to update so it crashes.
Is there a way to update my state properties for the inputs of the React Bootstrap form similar to how I used [event.target.name] : event.target.value for the regular Form?
To get its reference name, add values to the props name would be fine
Before:
<Form.Control type="text" placeholder="userId" ref={this.userId} onChange={this.handleInputChange}/>
After:
<Form.Control type="text" name="userId" placeholder="userId" ref={this.userId} onChange={this.handleInputChange}/>
I'm trying to find a neater way to handle this pattern I keep coming across with react when handling changes for form fields.
For each element of my form object that I handle a change in value for I find myself replicating this pattern quite a bit with the setter function of useState(). I've tried a couple of things like creating shallow copies of the formState and mutating that but the only way I can really get things to work is with the bellow pattern which feels a little repetitive.
const handleTitle = evt => {
props.setFormState({
title: evt.target.value,
bio: props.formState.bio,
formExpertise: props.formState.formExpertise,
formExpertiseYears: props.formState.formExpertiseYears
});
};
If you want to include this.props.formState you can spread the object into the new state. Further, you can use the input’s name as the state key so you don’t have to rewrite this for every input:
props.setFormState({
...this.props.formState, // copy props.formState in
[evt.target.name]: evt.target.value // use input name as state key
});
Suggestion:
You might consider moving the state merging up into the parent component:
// parent component
const [formState, setFormState] = React.useState({});
const onFieldChange = (field, value) => {
setFormState({
...formState,
[field]: value
});
}
return (
<MyFormComponent
formState={formState}
onFieldChange={onFieldChange}
/>
);
Each input can then invoke onFieldChange with the field name and value without concerning itself with the rest of the state:
function MyFormComponent ({onFieldChange}) {
const handler = ({target: {name, value}}) => onFieldChange(name, value);
return (
<div>
<input name="title" value={formState.title} onChange={handler} />
<input name="bio" value={formState.bio} onChange={handler} />
<input name="expertise" value={formState.expertise} onChange={handler} />
</div>
);
}
Given the source code for a simple login form, see below. You see I want to use the username text field's value when I click the form's submit button. Since I need a reference to the actual DOM node to retrieve the value, I'm setting the usernameElement variable to that node.
const Login = ({ onLogin }) => {
let usernameElement
const handleSubmit = event => {
event.preventDefault()
onLogin(usernameElement.value)
}
return <form onSubmit={handleSubmit}>
<input
type="text"
name="username"
ref={node => { usernameElement = node }}
/>
<button type="submit">Login</button>
</form>
}
So, how would I make an functional approach to that problem, or simply get rid of the let variable?
Apparently, the correct approach to this is to make use of the component's state, meaning you need a stateful component instead of the stateless one.
// untested
class Login extends Component {
state = { username: '' }
handleChange = event => {
this.setState({ username: event.target.value })
}
handleSubmit = event => {
event.preventDefault()
this.props.onLogin(this.state.username)
}
render = () =>
<form onSubmit={this.handleSubmit}>
<input
type="text"
name="username"
value={this.state.username}
onChange={this.handleChange}
/>
<button type="submit">Login</button>
</form>
}
Using this, the username is always bound to this.state.username. The handleSubmit method can just use the username from the component's state.
See this page for further information: https://facebook.github.io/react/docs/forms.html