In vanilla js if we want to add a listener to a dom we can do like this
<div onclick="myFunction(this)">...</div>
but wen i do it inside react component this is refers to component class itself. how to handle something like this?
You should pass event in your function, and then you access DOM element with e.target.
For example if you want to handle input change, you can do something like this:
const [values, setValues] = useState({
username: '',
});
const handleChange = event => {
event.persist();
setValues(values => ({
...values,
[event.target.id]: event.target.value
}));
};
event.target.id is the id of DOM element and event.target.value is the value of the same element, which in this case is input.
<input
id="username"
type="text"
placeholder="Enter username"
onChange={handleChange}
value={values.username}
required
></input>
Also
Note:
If you want to access the event properties in an asynchronous way, you should call event.persist() on the event, which will remove the synthetic event from the pool and allow references to the event to be retained by user code.
In react u should use onClick (with capital C) and pass the event to your function
<div onClick={(e) => {myfunction(e)}}>...</div>
Then in your function, use event.target to get the clicked tag.
const myfunction = (event) => {
let id = event.target.id;
}
In your context, this refers to the DOM target element, so you'd define your function as:
function myFunction(target) { /* Code here */ }
But in react, when you define an input:
<input onClick={myFunction} />
Your function is given an event not a target, so your function is defined as:
function myFunction(event) { console.log(event.target) }
Related
export default function Form() {
const [user, setUser] = useState({
name: "",
numOfQs: 0
})
console.log(user)
function handleUserDataChange(event) {
setUser(prevUser => {
return {
...prevUser,
[event.target.name]: event.target.value
}
})
}
return (
<>
<input
type="text"
placeholder="username"
name="name"
value={user.name}
onChange={handleUserDataChange} />
<input
type="number"
name="numOfQs"
value={user.numOfQs}
onChange={handleUserDataChange} />
</>
)}
I was trying to build my form using react, and when I tried to use input[type: number] on the form field it was giving me this error, don't know why. I was reading through react docs about forms, and everything from the checkbox, radio buttons, textarea was all working fine. but when I used an input element of the type number, I got the following error.
*!Warning: This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the property target on a released/nullified synthetic event. This is set to null. If you must keep the original synthetic event around, use event.persist(). See fb.me/react-event-pooling for more information.
so, the problem only arises when an input of type "number" is introduced. when I remove it all of my other form elements work fine.
I'm still in the learning phase of react. please help me out.
This happened because the event that passed into the function is used as an asynchronous event.
To fix this, decompose the event object
function handleUserDataChange(event) {
const { name, value } = event.target;
setUser(prevUser => {
return {
...prevUser,
[name]: value
}
})
}
Everytime i want to update some value in my state via a text input i just simply create an onChange function that accepts a event: React.ChangeEvent<HTMLInputElement | HtmlTextAreaElement> as an argument. Something like this:
const [state, setState] = useState<IState>({ email: "", password: "" });
const onChange = (event: React.ChangeEvent<HTMLInputElement | HtmlTextAreaElement>): void => {
const { name, value } = event.target;
setState({
...state,
[name]: string
});
}
And well it perfectly works but... i have to create this function every single time that i want to change my state via an input in other page (an onChange function in the login page, an onChange function in sign up page, forgotpassword page... etc). My question is: Is there a way to create a generic onChange that accepts any state and update it?
this is my first post on stack overflow, sorry if i dont follow the guidelines at all.
If the function is short and only used once, then one option you could consider would be to define it inline.
<input
name="name"
value={state.name}
onChange={e => { setState({ ...state, name: e.target.value }); }}
/>
You can also consider separating out states, as React recommends - there's no need to put everything into a single state variable (unless there are a lot of inputs in a form, in which case a single state object with a generic updater can make things more DRY).
const [name, setName] = useState(''); // No need for type annotations anymore
const [password, setPassword] = useState('');
// ...
<input
name="name"
value={name}
onChange={e => { setName(e.target.value); }}
/>
If you want a generic state updater that you don't have to redefine every time to have another state variable, you can create a higher order function that you pass in a state setter, that returns the change handler - then import this function wherever it's needed.
const makeChangeHandler = (stateSetter: React.Dispatch<React.SetStateAction<string>>) => {
return (event: React.ChangeEvent<HTMLInputElement | HtmlTextAreaElement>) => {
const { name, value } = event.target;
stateSetter({
...state,
[name]: value
});
};
};
<input
name="name"
value={name}
onChange={makeChangeHandler(setState)}
/>
Refactoring that function to deal with separate states instead of having a single state object is trivial, if desired, just do
stateSetter(value);
I am trying to change the input value on dynamically added input fields.
Each input field value is set to a state value which is made of an array.
Seems like there should be a simple solution for this.. But I can't just figure it out.
JSfiddle:
https://jsfiddle.net/o51Lkvm6/1/
handleInputChange = (e) => {
this.setState({
[e.target.name]: e.target.value
});
}
render() {
return (
<div>
{ this.state.data.map((d, index) =>
<input name={d.Name} type="text" className="form-control"
value={d.Name} onChange={this.handleInputChange} />
)}
</div>
);
}
Update:
Is it possible to solve this without having to use defaultvalue? Since React does not recommend "Uncontrolled Components"?
First of all there are couple issues with your code:
You forgot to bind your handler method or use arrow function to
preserve this context of a class. To fix that you can either put this
in Test constructor:
this.handleInputChange = this.handleInputChange.bind(this)
or modify your existing function to:
handleInputChange = e => {};
Input value should actually use the value which
corresponds to current item from state, like that:
value={this.state.data[index]["Name"]}
Later to access proper item in your stateData you have to somehow
store that index in the input. I did this by assigning it to
data-index attribute. Also you forgot to include key prop:
<input
key={d.ID}
data-index={index}
name={d.Name}
type="text"
className="form-control"
value={this.state.data[index]["Name"]}
onChange={this.handleInputChange}
/>
In your actual handleInputChange you were not targeting the correct
thing. You need to first get the appropriate item from the array and
then modify the name. I did it by copying the actual state and later
assigning it:
handleInputChange = e => {
const stateDataCopy = this.state.data.slice();
const objectCopy = Object.assign({}, stateDataCopy[e.target.dataset.index]);
objectCopy["Name"] = e.target.value;
stateDataCopy[e.target.dataset.index] = objectCopy;
this.setState({ data: stateDataCopy });
};
Here you can find working example:
ok I fixed it for you
do these 2 things
handleInputChange(e){ make this an arrow function so it has the concept of this like so: handleInputChange = (e) => {
and use defaultValue instead of value in the input
updated fiddle for you: https://jsfiddle.net/a17gywvp/1/
I have an input number value which I'm trying to send to my clickHandler but I've done something wrong ...
On click I want to send the value "this.state.NumberHolder" to the handler
<input value={this.state.NumberHolder} onClick={this.clickHandler} type="number" />
Doing a console.log I can see that my clickHandler is being called but I'm not getting the updated state value
clickHandler = (target) => {
console.log("targetHere", target);
this.setState({
NumberHolder: target.value
});
};
Actually, what you receive by default property is the context of the event.
So, to handle correctly the value of the input tag, you need to do this:
clickHandler = (event) => {
console.log("targetHere", event.target);
this.setState({
NumberHolder: event.target.value
});
};
And there is a big issue with your JSX, onClick is executed when the input is clicked, not changed. So, you will never receive the new input value. Use on change:
<input value={this.state.NumberHolder} onChange={this.clickHandler} type="number" />
And this should work perfectly. Check this fiddle to see it working.
I believe it should be like this:
// destructure target from the event object by wrapping it in braces
clickHandler = ({target}) => {
console.log("targetHere", target);
this.setState({
NumberHolder: target.value
});
};
But there is a bigger issue with your code. Since the value of your input will always be this.state.NumberHolder, you are simply setting the same value over and over again.
If you have a particular value you want to send on the click event, you can turn your click event into a curried function like this:
// pass in number as first argument to function that returns another anonymous function
clickHandler = (NumberHolder) => () =>{
this.setState({ NumberHolder });
};
And then on the element with the click event, pass the onClick like this:
<input onClick={this.clickHandler(3)} />
That will pass the argument in scope of the function, and allow you to access it.
Considering your comments, i believe you want to add the event on change of the input and log the value entered like this:
<input value={this.state.NumberHolder} onChange={this.clickHandler} type="number" />
clickHandler = (target) => {
console.log("target Value", target.value);
this.setState({
NumberHolder: target.value
});
};
I want to use the same onChange handler for a series of inputs.
<input onChange={this.handleInputChange}
type="text"
data-input-name="name"
value={this.state.name}/>
so I am attempting to use this html data attribute to store the input's name. When I go to pull the attribute off in JavaScript, I am unable to access it.
handleInputChange = (event) => {
this.setState([event.target.inputName]: event.target.value})
}
I've tried a few permutations to no avail, and it seems difficult to debug since when I log the event.target I just see an html element in the JavaScript console.
Any advice on how to better debug this or where my syntax is going wrong?
I've noticed that you have missed an opening curly brace in your setState call. And that will throw a syntax error when you run it. It should be fixed like this:
handleInputChange = (event) => {
this.setState({[event.target.inputName]: event.target.value})
}
For accessing your data attribute from the handleInputChange, you can do it like this:
handleInputChange = event => {
this.setState({
[event.target.getAttribute('data-input-name')]: event.target.value,
});
};
And also, you can use the default name attributes that comes with these inputs like this:
handleInputChange = event => {
this.setState({
[event.target.name]: event.target.value,
});
};
// in your render fucntion
<input
onChange={this.handleInputChange}
type="text"
name="name"
value={this.state.name}
/>
This will work as the same as using data attributes. Hope this help!
You could pass the input name as an argument instead of having it as a property, like so:
<input onChange={(e) => this.handleInputChange(e,"someValue")}
type="text"
value={this.state.name}/>
and then
handleInputChange = (event, name) => {
this.setState([name]: event.target.value})
}
I was also able to find a somewhat dirty solution to pulling the value off of the event object.
event.target.attributes['data-input-name'].value