Why a hook value is not the same as event target value? - reactjs

I have an input and when its value changes, I log it, but when I type "John" in the input, the "e.target.value" = "John", and "searchInputValue" = "Joh",
It seems like "searchInputValue" always have the last letter missing,
How can I really get the e.target.value have the same value as searchInputValue ?
const [searchInputValue, setSearchInputValue] = useState("");
function handleChange(e) {
setSearchInputValue(e.target.value);
console.log("target value ", e.target.value); <- return the good value
console.log("searchValue ", searchInputValue); <- return the value without
the last letter typed
}
<input
type="text"
onChange={e => handleChange(e)}
/>
Can someone help me ? Thank you

setSearchInputValue is asynchronous and also you get the new state in the next render. That is the reason you won't see it right away.
Also, you need your input to be controlled.
<input
type="text"
value = {searchInputValue}
onChange={e => handleChange(e)}
/>

I have faced the same problem when I was trying to use the React Hooks. The basic concept that you are missing here is that "setSearchInputValue" is an asynchronous function.
To get the updated value use "useEffect" hooks.
useEffect(() => {
console.log("searchValue ", searchInputValue);
},[searchInputValue]);

Related

suppress warning "Warning: You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field." in React

I have a warning that says
Warning: You provided a `value` prop to a form field without an `onChange` handler.
This will render a read-only field. If the field should be mutable use `defaultValue`.
`Otherwise, set either `onChange` or `readOnly`.`
Of course, I have googled a solution for it. One way to fix it is to replace "value" attribute with "defaultValues". But, this is causing another issue because my component is a controlled.
I could add onchange attribute and method to the fields. But the problem is that it will cause redundancy as I have a single onChange method on the tag that manages all the values and states. For example,
const [allStates, setAllStates] = useState({value_one: "", value_two:""});
function handleOnChange(event){
.........
//update the allStates state here
}
<Form onChange={handleOnChange}>
<input value={allStates.value_one} />
<input value={allStates.value_two} />
</Form>
What are the alternatives I have? Should I just ignore it since I can only see it on dev environments, the end users cannot and won't see it on the prod environment anyway?
It is a Helpful Warning by React, you can probably refer to this thread which discusses this.
https://github.com/facebook/react/issues/1118
Although adding the defaultValue should work for you, since your state has the latest value for the inputs, and when your component would render for the first time as long as the defaultValue is assigned to your <input>, the fields would reflect your state.
const [state, setState] = useState({});
const handleChange = (ev) => {
setState(prev_state => {
return {
...prev_state,
[ev.target.name]: ev.target.value
}
})
}
<form onChange={handleChange}>
<input name={"foo"} defaultValue={state.foo}/>
<input name={"bar"} defaultValue={state.bar}/>
</form>
The warning it is giving you is that you have provided the input with a value argument, so when you go to type in the input, it won't work:
const [allStates, setAllStates] = useState({
value_one: 'I will be display in the input initially', // <-- This text will already be entered in the text input.
value_two: ''
});
<input value={allStates.value_one} />
But since you added the value prop, you need to give it an onChange, otherwise the value of the field will always be the same and you won't be able to change it.
Ofcourse you can add the onChange like this:
const [allStates, setAllStates] = useState({
value_one: '',
value_two: ''
});
const handleOnChange = (event) => {
setAllStates({
...allStates,
// Now you can target the name prop
[event.target.name]: event.target.value
});
}
return (
<>
{/* I will suggest adding a name prop also */}
<input name="value_one" value={allStates.value_one} onChange={handleOnChange} />
<input name="value_two" value={allStates.value_two} onChange={handleOnChange} />
</>
);
The name prop is really useful for these one function setups, as it allows you to target the state value straight from the name.
But remember that the name values should be the same as the values in the state.

Is it possible to pass an additional parameter with an onChange event in React functional approach

I want to do something like,
<Input onChange={(e,myExtraParameter) => enteredData}/>
and then writing my onChange like,
enteredData = (e :React.FormEvent<HTMLTextAreaElement> | React.FormEvent<HTMLInputElement>,myExtraParameter : string ) => {
console.log("extra parameter = " , myExtraParameter);
}
That is, I want to check my key pressed input and also do something with it using an extra parameter.
I checked resources which says well about passing such option in React class-based approach where I can use "this" pointer. But how to do it in React functional component functional approach ?
Yes you can pass any value as long the function that receives it allows it otherwise wont get it.
example.
<input type="text" id="name" name="name" onChange={(event) => readName(event, "Miss")} />
Now the function
const readName = (event, gender) => {
const { target: { value } } = event;
console.log(value,gender);
}

Change value on dynamic input fields in reactjs

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/

setState not working as expected in react hook

When I am updating the with setState hook, value is not getting updated.
As per my knowledge this setState call is async. How to solve this issue?
export default function ResultItem({result:{ name, result_type, entry, unit, min_val, max_val, in_spec}, handleEntryChange}){
const [inSpec, setInSpec] = useState(in_spec)
const [result, setResult] = useState(entry);
console.log(handleEntryChange)
return(
<Fragment>
<td>{name}</td>
<td>{result_type}</td>
<td>{}</td>
<td>
<input
name={name}
type={result_type === 'numeric' ? "number" : "text"}
value={result || ''}
onChange={(e) => {
let val = parseInt(e.target.value)
setResult(val)
if(val >= min_val && val <= max_val){
setInSpec(true);
}else{
setInSpec(false);
}
console.log(result, inSpec, val)
handleEntryChange({[name]: val, [name]: inSpec});
}}
/>
</td>
<td>{unit}</td>
<td>{inSpec? 'YES': 'NO'}{console.log(in_spec)}</td>
</Fragment>
)
}
You are basically trying to make a custom Input. In general, you need to be careful of how this input is controlled ? On your interface, you have entry (which might be the initial value) but internally you have result.
Both are actually the value in Input context, but they normally are either controlled from external (on the interface) or tracked internally, but not both.
Just imaging if entry is changed, what could be result ?
The right way is the following
const Input = ({ value, onChange }) => {
// no internal state here
return (
<input value={value} onChange={onChange} />
)
}
This is a pure pass through model with both value and onChange driven by external.
Now you might ask how do I change the look and feel of my custom Input.
You can now write wrapper for onChange to inject your own implementation.
const onValueChange = e => {
// e.target.value
// do your own handling and then
// if need call onChange(e) to handle external
}
<input onChange={onValueChange} />
I'll skip the UI change for input here, since your example is already there.
The last comment here, the controlled input does not have storage. So in order to use your input, you need to have a state defined from external.
const [result, setResult] = useState(0)
return (
<YourInput value={result} />
)
The take home message here is WHO should manage the storage (memory), this might be the #1 fixture to put into place before designing any component.

Do not mutate state directly, Use setState() react/no-direct-mutation-state in React JS

<input
defaultValue={this.props.str.name}
ref={(input) => { this.state.name = input; }}
name="name"
type="text"
className="form-control"
onChange={this.handleInputChange}
/>
handleInputChange(event) {
this.setState({
[event.target.name]: event.target.value
});
}
if(this.state.name.value === "") {
this.msg.show('Required fields can not be empty', {
time: 2000,
type: 'info',
icon: <img src="img/avatars/info.png" role="presentation"/>
});
}
I'm trying to set the default value like that and wanted to access it as well. I did like this and accessed the value with this.state.name.value but the thing is its working but showing the warning as
Do not mutate state directly, Use setState()
react/no-direct-mutation-state .
Getting "Do not mutate state directly, Use setState()", Why?
Because, you are mutating the state value inside ref callback method to store the node ref, Here:
this.state.name = input;
Solution:
Don't use state variable to store the reference, You can directly store
them in component instance because that will not change with time.
As per DOC:
The state contains data specific to this component that may change
over time. The state is user-defined, and it should be a plain
JavaScript object.
If you don’t use it in render(), it shouldn’t be in the state. For
example, you can put timer IDs directly on the instance.
Since you are using controlled input element, ref is not required. Directly use this.state.name with input element value property and this.state.name to access the value.
Use this:
<input
value={this.state.name || ''}
name="name"
type="text"
className="form-control"
onChange={this.handleInputChange}
/>
If you wanted to use ref then store the ref directly on instance, remove value property and you can remove the onChange event also, Use it like this:
<input
ref={el => this.el = el}
defaultValue={this.props.str.name}
name="name"
type="text"
className="form-control"
/>
Now use this ref to access the value like this:
this.el.value
you can instead clone the entire property value inside the with spread operator and then reform or edit the value for example :
state = {Counters: [{id:1,value:1},{id: 2,value: 2},{id: 3,value: 3},{id: 4,value: 4}]}
increment = (Counter) => {
//This is where the state property value is cloned
const Counters = [...this.state.Counters];
console.log(Counters);
const index = Counters.indexOf(Counter)
Counters[index].value++
this.setState({
Counters: this.state.Counters
})
}
Change your line number 3 as
ref={(input) => { this.setState({name: input}); }}

Resources