Using a single handleInputChange method to for multiple input fields (React) - javascript

I have a form, in React"
render() {
return (
<div>
<form onSubmit={this.onFormSubmit}>
<input
value={this.state.first}
/>
<input
value={this.state.second}
/>
<input
value={this.state.third}
/>
//.... many more
</form>
//...
)}
My handleInputChange usually looks like this:
handleInputChange(e) {
this.setState({value: e.target.value });
}
Now, since I have many different input fields, i would normally do many different handleInputChange methods. However, all of these handle input change things basically do the same: they set the state anew, according to which input field is currently edited.
How could I, instead of writing three handleInputChange methods each doing something like:
handleInputChangeFirst(e) {
this.setState({first: e.target.value });
}
handleInputChangeSecond(e) {
this.setState({second: e.target.value });
}
... do all of this with one single handleInputChange, which then checks which value needs to be updated? How can i let handleInputChange know about the input field that is being edited and react accordingly?

You could have a generic handleInputChange method:
handleInputChange(property) {
return e => {
this.setState({
[property]: e.target.value
});
};
}
That you’d use as such:
<input
value={this.state.first}
onChange={this.handleInputChange('first')}
/>

Correct me if I'm wrong, but wouldn't the above method return a new function for each render? So if you pass these handlers down to a child component then they'd be seen as an updated prop every time instead of the same prop (cause it's a new function every render), and cause an unwanted re-render of the child. One of the reasons inline functions are hated in the render method.
Here's another solution I saw somewhere online:
handleInputChange = event => {
const { name, value } = event.target;
this.setState({
[name]: value
});
}
And the input element:
<input name="first" value={this.state.first} onChange={this.handleInputChange} />
Name might not be the best attribute to use, but you get the point

Related

React handle change function works without parameter event

I have created three components:
<MyInputComponent> extends the material ui component
<Input> handles the onChange of <MyInputComponent> by accepting as props onInputTyping and passing it to the onChange of <MyInputComponent>
<App> renders <Input> and passes to onInputTyping of <Input> a handleTypying function
Code: https://codesandbox.io/s/upbeat-bas-u0rj4
Inside Input I have:
const Input: React.FC<Props> = (props: Props) => {
const { onInputTyping } = props;
return (
<MyInputComponent
onChange={(event) => onInputTyping && onInputTyping(event)}
/>
);
};
Inside App I have:
export default function App() {
const handleTyping = (event: React.ChangeEvent<HTMLInputElement>) => {
console.log(event.target.value);
};
return (
<div className="App">
<Input onInputTyping={(event) => handleTyping(event)} />
<br />
<Input onInputTyping={() => handleTyping(event)} /> <-- This still console.log the event, but why?
<br />
<Input onInputTyping={handleTyping} /> <-- This still console.log the event, but why?
</div>
);
Question:
I am not understanding why the last two <Input> inside App are able to console.log the event that the user types.
I am passing the event only in the first Input, and this event is then passed to handleTyping. Example: <Input onInputTyping={(event) => handleTyping(event)} />
but
in the second and third <Input> I am not passing any event to handleTyping. So,
how is it possible that handleTyping is able to console.log(event) even if I am not passing any event to it?
Cause there is a global event variable. If you omit the parameter of the function, event will refer to that (in your second example). By coincidence the React.ChangeEvent that is received via the parameter and the global variable holding an HTMLEvent have the same properties, that's why it "works".
Your third example is not much different than your first which I think can best be illustrated by removing all the React around it and just showing the functions:
function realHandler(event) {
//...
}
function callback(handler) {
handler(event); // this is doing exactly the same thing as the following line
realHandler(event);
}
callback(realHandler);

How can I setState() another input value with a button in the same component in React?

How can I setState() another input value with a button in the same component in React?
I'm using the onClick event handler on the button.
I want to make the handleClickfunction which I gave it to the button, to target the value of the input
class Search extends Component {
state = {
searchInput: "",
};
handleClick = () => {
this.setState({
searchInput: input.value,
});
};
render() {
return (
<div>
<input type="text"/>
<button onClick={this.handleClick}>Enter</button>
</div>
);
}
}
Your question is not clear, I believe you are asking how to set the value of an input field when you press a button in react.
If that is correct, then you have done most of the work already, all you need to do now is add an <input> tag.
Like this:
<input type="text" value={ this.state.searchInput } />
If I have misunderstood your question then please clarify.
It may be worth reading about how State and Lifecycle work in React Here
Whenever the setState() function is triggered, React automatically runs the render() function in any components where state has changed, rerendering that component with the new state values.
Edit
After clarification I now understand exactly what you want.
You require the use of a ref, like this:
class Search extends Component {
state = {
searchInput: "",
};
handleClick = () => {
this.setState({
searchInput: this.inputText,
});
};
render() {
return (
<div>
<input type="text" ref={(x) => this.inputText = x}/>
<button onClick={this.handleClick}>Enter</button>
</div>
);
}
}
instead of using a button to update the state try this:
<input type="text" onChange={(e) => this.setState({searchInput: e.target.value }) />

Passing dynamic onChange listener to children

I have a stateful component that holds some state
state={
name:'',
age:'',
occupation:''
}
And a function to update the state onChange listener
onValueChange = (key, event) => {
this.setState({ [key]: event.target.value });
};
I pass the state and function down to child as props
<ComponentA {...this.state} changed={this.onValueChange}>
Inside my component B which is a child of A I want to programatically create inputs based on given props and change the state by invoking that function every time user types in input.
<ComponentB>
{ Object.entries(this.props)
.filter(
prop =>
prop[0] !== 'changed'
)
.map(propName => (
<Input
key={propName}
label={propName[0]}
value={propName[1]}
onValueChange={this.props.changed(propName[0])}
/>
))}
</ComponentB>
My Input component just renders the following
<input
onChange={this.props.onValueChange}
value={this.props.value}
type={this.props.type}
placeholder=" "
/>
Can't make it work for some reason.
Thanks for any help!
You are currently invoking this.props.changed straight away on render by writing onValueChange={this.props.changed(propName[0])}. Instead of invoking it on render you should give it a function to call when onValueChange occurs instead.
You also want to give the Input a unique key prop that will not change between state updates, so that React doesn't create an entirely new component every time and you e.g. lose focus of the input. You can use propName[0] instead, which will be unique.
<Input
key={propName[0]}
label={propName[0]}
value={propName[1]}
onValueChange={event => this.props.changed(propName[0], event)}
/>

React Unknown Prop 'onSearch'

I'm trying to use onSearch event on a search input like this:
render(){
return(
<input type="search" onSearch={ () => console.warn('search') } />
);
}
But I get the unknown prop error, any ideas of how to fix it?
I want to create a autocomplete input that fires an event when the user clicks on the "search" icon in the keyboard.
EDIT:
onSearch is not an standard attribute so I guess react wont support it. Since onSearch is the same as hitting enter key, using onKeypress will do the job:
render() {
return (
<main>
<input onKeyPress={ this.onKeyPress } type="search" list="options"/>
<datalist id="options">
{ OPTS.map(item => <option value={ item.nm } key={ item.id } data-id={ item.id } />) }
</datalist>
</main>
);
}
onKeyPress(event) {
if (event.key === 'Enter') {
const id = document.querySelector(`#options option[value="${event.target.value}"]`).dataset.id;
if (id) {
console.warn('user search', id);
}
}
}
search is absolutely a DOM event of search type input but react doesn't support it yet.
so you may listen to raw dom element events.
do like this
<Input
ref={element=>(element||{}).onsearch=this.searchHandler}
/>
this can work, you may want to know why.
First, ref attribute accept a function as a value, which will be executed with a real dom element(opposite to virtual dom).
And before the real dom be mounted to document the function will be executed too, but there's no element we can attach an event to, so we use (element||{}) to avoid errors.
Instead of using addEventListener, we use ele.onsearch way, because the component will rerender when props or state update, each time the render function be called we will attach an event to the dom element when search event fired the handler function can be run several times.
I think you are looking for onChange. An input will fire a change event when you edit the data inside it.
EDIT:
onSearch is not supported by React Docs you can use a keypress event or keyup event to do the same thing. just check the keycode and see if its an enter (13)
handleKeyPress = (e) => {
if(e.keyCode === 13) {
console.warn('search');
}
}
<input type="search" onKeyPress={this.handleKeyPress} />
I think something like this should work:
constructor() {
this.state = { userInput: "" };
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit() {
console.warn('user search', this.state.userInput);
}
render() {
return (
<form onSubmit={handleSubmit}>
<input
onChange={e => this.setState({userInput: e.target.value})}
type="search"
list="options"
value={this.state.userInput}
/>
<datalist id="options">
{ OPTS.map(item => <option value={ item.nm } key={ item.id } data-id={ item.id } />) }
</datalist>
</form>
);
}
Take a look at https://facebook.github.io/react/docs/forms.html. You probably want to use controlled components instead of accessing the DOM directly to get the input value.

to call onChange event after pressing Enter key

I am new to Bootstrap and stuck with this problem. I have an input field and as soon as I enter just one digit, the function from onChange is called, but I want it to be called when I push 'Enter when the whole number has been entered. The same problem for the validation function - it calls too soon.
var inputProcent = React.CreateElement(bootstrap.Input, {type: "text",
//bsStyle: this.validationInputFactor(),
placeholder: this.initialFactor,
className: "input-block-level",
onChange: this.handleInput,
block: true,
addonBefore: '%',
ref:'input',
hasFeedback: true
});
According to React Doc, you could listen to keyboard events, like onKeyPress or onKeyUp, not onChange.
var Input = React.createClass({
render: function () {
return <input type="text" onKeyDown={this._handleKeyDown} />;
},
_handleKeyDown: function(e) {
if (e.key === 'Enter') {
console.log('do validate');
}
}
});
Update: Use React.Component
Here is the code using React.Component which does the same thing
class Input extends React.Component {
_handleKeyDown = (e) => {
if (e.key === 'Enter') {
console.log('do validate');
}
}
render() {
return <input type="text" onKeyDown={this._handleKeyDown} />
}
}
Here is the jsfiddle.
Update 2: Use a functional component
const Input = () => {
const handleKeyDown = (event) => {
if (event.key === 'Enter') {
console.log('do validate')
}
}
return <input type="text" onKeyDown={handleKeyDown} />
}
You can use onKeyPress directly on input field. onChange function changes state value on every input field change and after Enter is pressed it will call a function search().
<input
type="text"
placeholder="Search..."
onChange={event => {this.setState({query: event.target.value})}}
onKeyPress={event => {
if (event.key === 'Enter') {
this.search()
}
}}
/>
Pressing Enter in a form control (input) normally triggers a submit (onSubmit) event on the form. Considering that you can handle it this way (having a submit button is optional if you have only one input):
const { useState } = React;
function App() {
const [text, setText] = useState("");
const [submitted, setSubmitted] = useState('');
function handleChange(e) {
setText(e.target.value);
}
function handleSubmit(e) {
e.preventDefault();
setSubmitted(text);
setText("");
}
return (
<div>
<form onSubmit={handleSubmit}>
<input type="text" value={text} onChange={handleChange} />
<input type="submit" value="add" />
</form>
submitted: {submitted}
</div>
);
}
ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://unpkg.com/react#17.0.2/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#17.0.2/umd/react-dom.development.js"></script>
<div id="root"></div>
Implicit form submission (submit event on Enter) is performed when:
there's a submit button
there're no submit buttons, but there's only one input
More on it here.
Alternatively you could bind your handler to the blur (onBlur) event on the input which happens when the focus is removed (e.g. tabbing to the next element that can get focus).
You can use event.key
function Input({onKeyPress}) {
return (
<div>
<h2>Input</h2>
<input type="text" onKeyPress={onKeyPress}/>
</div>
)
}
class Form extends React.Component {
state = {value:""}
handleKeyPress = (e) => {
if (e.key === 'Enter') {
this.setState({value:e.target.value})
}
}
render() {
return (
<section>
<Input onKeyPress={this.handleKeyPress}/>
<br/>
<output>{this.state.value}</output>
</section>
);
}
}
ReactDOM.render(
<Form />,
document.getElementById("react")
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="react"></div>
React users, here's an answer for completeness.
React version 16.4.2
You either want to update for every keystroke, or get the value only at submit. Adding the key events to the component works, but there are alternatives as recommended in the official docs.
Controlled vs Uncontrolled components
Controlled
From the Docs - Forms and Controlled components:
In HTML, form elements such as input, textarea, and select typically
maintain their own state and update it based on user input. In React,
mutable state is typically kept in the state property of components,
and only updated with setState().
We can combine the two by making the React state be the “single source
of truth”. Then the React component that renders a form also controls
what happens in that form on subsequent user input. An input form
element whose value is controlled by React in this way is called a
“controlled component”.
If you use a controlled component you will have to keep the state updated for every change to the value. For this to happen, you bind an event handler to the component. In the docs' examples, usually the onChange event.
Example:
1) Bind event handler in constructor (value kept in state)
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
}
2) Create handler function
handleChange(event) {
this.setState({value: event.target.value});
}
3) Create form submit function (value is taken from the state)
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
4) Render
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
If you use controlled components, your handleChange function will always be fired, in order to update and keep the proper state. The state will always have the updated value, and when the form is submitted, the value will be taken from the state. This might be a con if your form is very long, because you will have to create a function for every component, or write a simple one that handles every component's change of value.
Uncontrolled
From the Docs - Uncontrolled component
In most cases, we recommend using controlled components to implement
forms. In a controlled component, form data is handled by a React
component. The alternative is uncontrolled components, where form data
is handled by the DOM itself.
To write an uncontrolled component, instead of writing an event
handler for every state update, you can use a ref to get form values
from the DOM.
The main difference here is that you don't use the onChange function, but rather the onSubmit of the form to get the values, and validate if neccessary.
Example:
1) Bind event handler and create ref to input in constructor (no value kept in state)
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.input = React.createRef();
}
2) Create form submit function (value is taken from the DOM component)
handleSubmit(event) {
alert('A name was submitted: ' + this.input.current.value);
event.preventDefault();
}
3) Render
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" ref={this.input} />
</label>
<input type="submit" value="Submit" />
</form>
If you use uncontrolled components, there is no need to bind a handleChange function. When the form is submitted, the value will be taken from the DOM and the neccessary validations can happen at this point. No need to create any handler functions for any of the input components as well.
Your issue
Now, for your issue:
... I want it to be called when I push 'Enter when the whole number has been entered
If you want to achieve this, use an uncontrolled component. Don't create the onChange handlers if it is not necessary. The enter key will submit the form and the handleSubmit function will be fired.
Changes you need to do:
Remove the onChange call in your element
var inputProcent = React.CreateElement(bootstrap.Input, {type: "text",
// bsStyle: this.validationInputFactor(),
placeholder: this.initialFactor,
className: "input-block-level",
// onChange: this.handleInput,
block: true,
addonBefore: '%',
ref:'input',
hasFeedback: true
});
Handle the form submit and validate your input. You need to get the value from your element in the form submit function and then validate. Make sure you create the reference to your element in the constructor.
handleSubmit(event) {
// Get value of input field
let value = this.input.current.value;
event.preventDefault();
// Validate 'value' and submit using your own api or something
}
Example use of an uncontrolled component:
class NameForm extends React.Component {
constructor(props) {
super(props);
// bind submit function
this.handleSubmit = this.handleSubmit.bind(this);
// create reference to input field
this.input = React.createRef();
}
handleSubmit(event) {
// Get value of input field
let value = this.input.current.value;
console.log('value in input field: ' + value );
event.preventDefault();
// Validate 'value' and submit using your own api or something
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" ref={this.input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
ReactDOM.render(
<NameForm />,
document.getElementById('root')
);
You can also write a little wrapper function like this
const onEnter = (event, callback) => event.key === 'Enter' && callback()
Then consume it on your inputs
<input
type="text"
placeholder="Title of todo"
onChange={e => setName(e.target.value)}
onKeyPress={e => onEnter(e, addItem)}/>
I prefer onKeyUp since it only fires when the key is released. onKeyDown, on the other hand, will fire multiple times if for some reason the user presses and holds the key. For example, when listening for "pressing" the Enter key to make a network request, you don't want that to fire multiple times since it can be expensive.
// handler could be passed as a prop
<input type="text" onKeyUp={handleKeyPress} />
handleKeyPress(e) {
if (e.key === 'Enter') {
// do whatever
}
}
Also, stay away from keyCode since it will be deprecated some time.
Example of preventing Enter from submitting a form on an input, in my case it was a google maps location autocomplete input
<input
ref={addressInputRef}
type="text"
name="event[location]"
className="w-full"
defaultValue={location}
onChange={(value) => setLocation(value)}
onKeyDown={(e) => {
if (e.code === "Enter") {
e.preventDefault()
}
}}
/>
Here is a common use case using class-based components: The parent component provides a callback function, the child component renders the input box, and when the user presses Enter, we pass the user's input to the parent.
class ParentComponent extends React.Component {
processInput(value) {
alert('Parent got the input: '+value);
}
render() {
return (
<div>
<ChildComponent handleInput={(value) => this.processInput(value)} />
</div>
)
}
}
class ChildComponent extends React.Component {
constructor(props) {
super(props);
this.handleKeyDown = this.handleKeyDown.bind(this);
}
handleKeyDown(e) {
if (e.key === 'Enter') {
this.props.handleInput(e.target.value);
}
}
render() {
return (
<div>
<input onKeyDown={this.handleKeyDown} />
</div>
)
}
}
const [value, setValue] = useState("");
const handleOnChange = (e) => {
setValue(e.target.value);
};
const handleSubmit = (e) => {
e.preventDefault();
addTodoItem(value.trim());
setValue("");
};
return (
<form onSubmit={handleSubmit}>
<input value={value} onChange={handleOnChange}></input>
</form>
);
//You can use onkeyup directly on input field
const inputField = document.querySelector("input");
inputField.addEventListener("keyup", e => {
if (e.key == "Enter") {
console.log("hello");
}
});

Categories

Resources