In Vue.js :
By default, v-model syncs the input with the data after each input event (with the exception of IME composition as stated above). You can add the lazy modifier to instead sync after change events:
<!-- synced after "change" instead of "input" -->
<input v-model.lazy="msg" >
Does react has the similar modifier or function?In which part of the official doc?
The following is from Vue docs for v-model:
Although a bit magical, v-model is essentially syntax sugar for
updating data on user input events
In React, you can listen to any input event ( onChange, onClick, etc. ) and trigger a function that updates the React Component's state. If you want to pass the data down, you can pass it as props to any children. In this way we can keep data updated with input events. For more info, see React State and React Component and Props
onChange (in React) will do exactly what v-model do in VueJS.
Vue <input type="text" v-model="record" />
React <input type="text" name="record" onChange={e => setRecord(e.target.value)} />
You can use onBlur event on input field in order to achieve functionality similar to v-model.lazy in VueJS.
Vue <input type="text" v-model.lazy="record" />
React <input type="text" name="record" onBlur={e => setRecord(e.target.value)} />
The .lazy modifier in Vue.js will sync on DOM's change events instead of input. DOM's change events occur in a variety of situations depending on the form element used. A check box or radio button will trigger a change event when it’s clicked. An input text box will trigger a change event when it loses focus. Different browsers might not trigger change events on the same interactions.
However, React's onChange works differently than onChange in the DOM: it’s more like DOM's onInput. React's onChange fires when there is a change in any of the form’s input elements, in contrast to the DOM’s change event.
In React.js, if you'd like to have the similar behaviour as Vue.js' v-model.lazy, you need to bind the event handler to a different event.
In regard to your example, in Vue.js we have:
<!-- synced after "change" instead of "input" -->
<input v-model.lazy="msg" >
In React.js, we cannot use onChange, as the onChange handler is fired on every key stroke, not just when the whole input field has changed. React.js' onChange is not "lazy" :-) Instead, you can use its onBlur event:
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.state = {
msg: ''
}
}
handleBlur(event) {
this.setState({msg: event.target.value});
}
render() {
return <div>
<input type="text" onBlur={this.handleBlur.bind(this)}/>
<p>Your message: {this.state.msg}</p>
</div>
}
});
Here's the example on jsfiddle.
Related
EDIT: solved by a react-hook-form module update.
Problem
react-hook-form does not update form input state dirty, touched on custom components where name is dynamically passed to register. Specifically, field array inputs where the name is something like ingredients.0.description will track the input value but not the input state. Simple names like question or name seem to behave correctly.
Reproduction of the bug on this CodeSandbox.io environment.
Steps to reproduce:
Type or change the value in the "name" field (on the first line)
Type or change the value in any of the "description" fields (the field array)
Open react-hook-form DevTools (the pink icon in the bottom right of the browser)
See that the ingredients fields did not update dirty or touched values despite not matching defaultValue and having blurred off the input
See that the name field shows dirty: true, touched: true correctly
Context
I'm using react-hook-form and running into problems when integrating it with a custom uncontrolled component.
In most (all?) of the examples I see, the register function is either applied to a native HTML Input Element as one or the other:
<input {...register("field_name")} />
<input ref={register} name="field_name" />
Because I have some custom input styling, I'd like to make reusable components that integrate into react-hook-form, and though I could do something like:
// Input.tsx
export function Input(props) {
type Props = {
type: string;
name: string;
};
export function Input(props: Props) {
const { type, name } = props;
const { register, getValues, formState } = useFormContext();
return <input type={type} {...register(name)} />;
}
// App.tsx
<FormProvider {...methods} >
<form onSubmit={handleSubmit(onSubmit)}>
<Input name="field_name" type="text" />
</form>
<FormProvider/>
Questions
Am I using register incorrectly?
Can register take a name dynamically or must it be a static string? Is my field array name description malformed?
Do uncontrolled component methods need to be applied directly to an <input /> or is it possible to wrap with a custom component and pass the props? What's the most idiomatic way to do this with react-hook-form?
How can I modify the code in my CodeSandbox to correctly reflect all field inputs' states?
import React, { useState } from "react";
function App() {
const [name, setName] = useState("");
const [headingText, setHeadingText] = useState("");
function handleChange(event) {
// console.log(event.target.value);
setName(event.target.value);
}
function handleClick(event) {
setHeadingText(name);
//after everything is done
event.preventDefault();
}
return (
<div className="container">
<h1>Hello {headingText}</h1>
<form onSubmit={handleClick}>
<input
onChange={handleChange}
type="text"
placeholder="What's your name?"
value={name}
/>
<button type="submit">Submit</button>
</form>
</div>
);
}
export default App;
I will use the above code for asking my question.
input element handles its state change via user input(value attribute) but react uses state variable to handle state change. In order to have one constant value we set react variable to handle state change of form element as well.
value={name}
but if we do so and then access the value entered by the user through event.target.value, How is it able to track what user enters in the input field as we have set value={name}. Shouldn't event.target.value give us the value of name variable instead of user input
I think like this.
value={name} is external state for input element. In React term it's props.
And event.target.value comes from the internal state of input element right after the user keypress happens. In react terms it's a state.
So you change your internal state from user input. It's event.target.value
And you set input element's prop when parent component rerenders. it's value={name}
The answer is that react is 1 way data flow. It's not bound both ways like angularjs or Angular [(ngModel)] is. Meaning that internal DOM element state updates do not take effect until the component is re-rendered - and the component will not re-render until the component state is changed. Therefore, the value in the input is not being "set" by react until state changes. You can modify the value in the input as much as you'd like, and react won't do anything until the onChange handler that then updates state is triggered. Only then will it re-render the component and put the new value in the input box.
Until the re-render happens, the react state and the internal state of the <input> element are out of sync. event.target.value reads back the internal state of the input element, whereas {name} reads back the value of the name property on the, in this case, App component. The two get resync'd when the component is re-rendered.
Currently I am working on a form with the use of react hook forms and I want to save input from a user in the localstorage. At this moment whenever I change the value of the input when loaded from localstorage the input wont change.
When I console.log the event from the onchange method I don't get a log so it didn't trigger the onchange and thats where my problem is. I want that when a user wants to change anything in the form that it should update again.
I have searched on google but I can't find any related problem and so no solution. Since I am a beginner in react I have no clue to solve it myself.
The onchange function is in a functional component same as the input components is only functional.
This is the onchange function that contains the input event.
const onChange = event => {
localStorage.setItem(event.target.id, event.target.value);
};
This is the input compontent
<Input key={index} field={item} formFunction={register({required:true})} checkChange={handleChange} onChange={e => onChange(e)} value={localStorage.getItem(item.label) || ''} errors={errors} selectCheck={handleCountryChange} touched={touched} />
And this is the input compontents code
return (
<div className={props.field.grid}>
<div className={props.field.inputClass}>
<input
type={props.field.type}
name={props.field.name}
className={props.field.class}
id={props.field.label}
data-save="persist"
onBlur={props.checkChange}
style={{ marginBottom: '5px' }}
onChange={props.onChange}
value={props.value}
/>
<label className="left" htmlFor={props.field.label}>{props.field.label}</label>
</div>
</div>
);
Your problem is you are using the local storage to try and update the state of the app so the render function will not get re called and display the new inputted. ( unless you can show more code and you are indeed updating that this.props.value )?
I would suggest looking up local state within component for react, it will make things 10x easier in the future:
React state
Functional component state
You are best creating a local state in your constructor if it is an class component e.g., same can be achieved if it is a functional component just slightly different.
this.state = {
inputVariable: ""
}
then when ever your change this variable(in your onchange function using set state):
setstate({
inputVariable: valueToUpdate
})
your input components value field should be populated with this.state.inputVariable, so as you change the value it will trigger on change and then update the state which will cause a re render of your UI.
if you additionally also to save it to local storage you can do so like you already have.
You can see below sample code, it's a typical controlled field.
export default class NameForm extends Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
return (
<form>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
I can understand that if I type any text in the field, the value will be reflected into the text field due to the setState function and the value props of the input.
But if I changed the input to below. I removed the value props.
<input type="text" onChange={this.handleChange} />
Currently, If I typed any value in the field, the value will be still there, but as per my knowledge of React, the input will be rendered after the setState, but the props value is not set, the input will be cleared in my opinion, Could you please explain it in detail for me?
I think you miss the important point here. After setState is called the component render will be called - but this will not recreate the input component from scratch. Internally react will check and apply only changes that has happened to the actual DOM.
In your case no differences to the input were made - therefore it will stay as it is.
This process is called reconciliation and you can read more about it here.
Setting value on an input element makes it a controlled input. Controlled inputs always show the value provided in their prop. For example if you have this input:
<input value='constant' onChange={({target: {value}}) => this.setState({value})}/>
User can't change its value. but if you don't provide value prop for your input it's not a controlled value and it shows the value user enters in. This same thing is true for checkboxes, radio buttons, etc.
So in your case it updates based on user input because it's not a controlled component (because you didn't provide value for it.)
If you want to set initial value for a component but keep it uncontrolled (user can change the value of component) you can set the initial value with defaultValue prop.
You can read more here:
https://facebook.github.io/react/docs/forms.html
You shouldn't be setting the value, just the defaultValue, like this
<input type="text" defaultValue={this.state.value}
The handleChange will then make sure the state is updated
The JSX:
var Button = require("react-bootstrap").Button;
var Input = require("react-bootstrap").Input;
var MyClass = React.createClass({
onclick: function () {
this.refs.email.getDOMNode().focus();
console.log('DOM element', this.refs.email.getDOMNode());
}
render: function () {
return (
<div>
<Button onClick={this.onlick}>Login</Button>
<Input ref='email' type='email' />
</div>
);
},
});
Notes:
The input field is a react-bootstrap.Input class.
The getDOMNode().focus() concept is from Facebook react docs
When the button onclick handler runs, the email input field should be getting the focus, but it's not happening. I found that the react-bootstrap Input field class is rendering the real DOM input field inside a wrapping div. This is the output of the console.log:
<div class="form-group" data-reactid=".1awy8d4e9kw.1.3.$=1$3:0.0.0.1.0">
<input class="form-control" type="email" data-reactid=".1awy8d4e9kw.1.3.$=1$3:0.0.0.1.0.1:$input">
</div>
So looks like the input isn't getting the focus because the focus is applied on the wrapping div, which rejects it (I use document.activeElement in the console to check).
Question is, how to focus on the real input element?
Note:
A bit unrelated but React respects the autoFocus property. That's useful in case the focus is needed to be set immediately after rendering. Still, it's not a substitute for the programmatic way.
Try getInputDOMNode() instead of getDOMNode().
in render
<FormControl
type='text'
ref={(c)=>this.formControlRef=c}
/>
in event handler
someHandler() {
ReactDOM.findDOMNode(this.formControlRef).focus();
}
Since findDOMNode is no longer the suggested method and may be deprecated by Facebook, you should use the autoFocus property (which is provided by the React framework, not react-bootstrap). This is a cross-browser polyfill for the HTML autofocus attribute.
<FormControl
autoFocus
...
/>
Sources:
Focusing to the FormControl
Support needed for focusing inputs in modals?
FormControl provides inputRef attribute
try it,
<FormControl inputRef={ref => { this.input = ref; }} />
https://react-bootstrap.github.io/components/forms/
or you may use vanilla javascript and select the element and set focus
document.getElementById("myInput").focus()