setState not working as expected in react hook - reactjs

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.

Related

Input attribute doesn't update when const change value

The value of pickedTrue changes from false to true as the component is mounting. but the input defaultChecked doesn't update it just sets as false.
page.js:
function page() {
return (
<Checkbox checked={'1'} />
)
}
export default page
checkbox component:
function Checkbox({checked}) {
const pickedTrue = checked == '1' ? true : false
console.log(pickedTrue)
return (
<input type="checkbox" defaultChecked={pickedTrue}/>
)
}
export default Checkbox
console.log(pickedTrue) output:
undefined
false
undefined
undefined
true
*note: if i use === instead of == it results in false either way.
Use a state variable instead of a constant value. To effectively log changes to your variable you can best use useEffect with a dependency. In your dependency use checked instead of defaultChecked and add an onChange-function so your input will update when you click on the checkbox.
function Checkbox({defaultChecked}) {
const [checked, setChecked] = useState(defaultChecked === '1');
useEffect(() => {
console.log(checked);
}, [checked])
return (
<input type="checkbox"
checked={checked}
onChange={() => setChecked(!checked)}
/>
)
}
You can handle it with useState. Because the state rerender every change.
...
const [checked, setChecked] = useState(false);
console.log(checked);
return (
<input
type="checkbox"
onChange={() => setChecked(!checked)}
defaultChecked={checked}
/>
);
demo
Like everyone else suggested, you might wanna keep track of values in a local state.
Anyway, here's my take on your problem.
It can accept 0 and 1 and sets the checkboxes accordingly.
CodeSandbox - https://codesandbox.io/s/musing-lovelace-41y75?file=/src/Checkbox.jsx
Ref - https://www.robinwieruch.de/react-checkbox/
You are trying to reassign the value of constant which cannot be done. as the value once assigned cannot be changed. And like most of the answers, you should use the state to store the value even when it is coming from the database. You can create a separate function and call it for the defaultChecked. You can do something like this -
function Checkbox({checked}) {
const [checkedValue, setCheckedValue] = useState(checked === 1 ? true : false)
const setChecked = () => {
setChecked(!checked)
}
return (
<input type="checkbox" defaultChecked={checkedValue} onClick={setChecked}/>
)
}
export default Checkbox
Always try using the === strict equality operator which sure the answer isn't anything else but the one with which its type is matched against. When you used the strict equality operator it returned false because you sent a string 1 as a prop and were checking it against the integer 1.

react set value to another component when change value in different component

i have two react input components, where in one input component; value changes, i have to set the same value in another input field.
here is my two input components.
<div className="col-md-4">
<label htmlFor="saleamount">Payment </label>
<NumberFormat id="manualPaymentEntry" name="manualPaymentEntry" fixedDecimalScale={true} decimalScale={2} value={manualPayEntry.current} className="form-control" thousandSeparator={true} thousandsGroupStyle="lakh" onChange={(values) => {
const { formattedValue, value } = values;
handleManualPaymentEntry(value);
}} />
</div>
<div className="col-md-4">
<label htmlFor="selectedPayment">Actual Payment</label>
<NumberFormat readOnly={true} fixedDecimalScale={true} decimalScale={2} value={cumulativePay} className="form-control" thousandSeparator={true} thousandsGroupStyle="lakh" />
</div>
</div>
when there is change in manualPaymentEntry i have to set same value to cumulativePay
const [cumulativePay, setCumulativePay] = useState(0);
const manualPayEntry = useRef(0);
useEffect(() => {
setCumulativePay(manualPayEntry.current);
}, [manualPayEntry]);
const handleManualPaymentEntry = (value) => {
let val = parseFloat(value);
manualPayEntry.current = value;
}
i have used useRef for manualPayEntry to set current value to cumulativePay, but when there is change in manualPayEntry it is not setting value to cumulativePay, always i am getting '0' only..., but current value of manualPayEntry shouled get refelcted in cumulativePay
issue got resolved, after chaning onChange to onValueChange, as said in the documentation onchange will no longer get value , instead we need to use onValueChange
react-number-format npm
onValueChange is not same as onChange. It gets called on whenever
there is change in value which can be caused by any event like change
or blur event or by a prop change. It no longer receives event object
as second parameter.
Changing a value manually like you are doing it there:
manualPayEntry.current = value;
Does not trigger the rerendering functionalities of React. Why don't you just add the setCumulativePay directly in the handleManualPaymentEntry?
const handleManualPaymentEntry = (value) => {
let val = parseFloat(value);
manualPayEntry.current = value;
setCumulativePay(value);
}
Or you should treat manualPayEntry as a state as well and not a ref, though it's not really advised to change a state in a useEffect based on another state. I really think changing directly the needed state in your handler function is the best solution.

React Native: Reset children states from grandfather?

This is a tricky question. Lets say I have a Component <GrandFather /> with a <Form /> Component inside. The <Form /> has Multiple <Input /> Components. The <Input /> has an internal useState. At the <GrandFather />, there are some functions like loadForm(loading some fields) and unloadForm(removes these fields). On the unloadForm I wanna reset the internal State of the Inputs. The only solution I found was to have a key on the <Form /> and increment it on unload, so thats forces the rest. Is there a better good way to do this without change the logic? P.S I 'm using Typescript.
function GrandFather (props: Props) {
const loadForm = () => // load some fields to the formData
const unloadForm = () => // unload these fields to the formData
return <Form formData={formData}/>
}
function Form (formData: FormData) {
return (
<>
<Input /> // with some props
<Input /> // with some props
<Input /> // with some props
</>
)
}
function Input (props: Props) {
const [state, setState] = useState(false);
// the state here is being used for styling and animations, at
// somepoint it will became true
return <TextInput {...props}/>
}
any way here to reset this state to all the inputs on the function unloadForm?
I see two different methods of achieving this.
Method #1
As an example, I'll use a simple login form.
So you could define the following state variables on GrandParent:
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
Then, pass all username, setUsername, password, and setPassword as props to Form and then on to the Input components.
Inside of the Input component, you could turn the inputs into controlled inputs by adding the following:
<input type="text" value={username} onChange={setUsername} />
If you ever need to clear the inputs, you could call right directly from GrandParent or Form (or anywhere where you have access to the setters) the following:
setUsername('');
setPassword('');
Method #2 (hacky)
You could define, on GrandParent, a state variable that, as the above method, you would pass both the variable and setter as props to Form and then to each Input:
const [clear, setClear] = useState(false);
Then, inside the Input component, assuming you have a value state variable on it (with the equivalent setter setValue), you could set up a listener for a change on the state variable clear:
useEffect(() => {
if (clear === true) {
setValue('');
}
}, [clear]);
Then, whenever you wanted to clear all the input values, you could call, from anywhere:
setClear(true);
setTimeout(() => { // the setTimeout might not be required
setClear(false);
}, 1);

Can re-rendering of all inputs be avoided when passing a whole object?

I have quite a few different interfaces with many properties like
interface IEntity1 {
a: string
b: number
c: string
....
}
When I want to edit an object (i.e., create a modified copy), I can do
function Editor(props: {entity: IEntity1, setEntity(e: IEntity1) => void}) {
const {entity, setEntity} = props;
const [a, setA] = useState(entity.a);
const [b, setB] = useState(entity.a);
....
function handleOk() {
setEntity({a, b, c, ...});
}
return <div>
<input value={a} onChange={e => setA(e.target.value}/>
<input value={''+b} onChange={e => setB(+e.target.value}/>
<input value={c} onChange={e => setC(e.target.value}/>
...
<button onClick={handleOk}>OK</button>
</div>;
}
This works fine, but it's pretty repetitive. So I created a "universal" component, which can handle number, string and other inputs. Using it, I can handle all fields in a loop like
{fieldNames.map(f => <Edit value={entity[f]}
onChange={newValue => setEntity({...entity}, {[f]: newValue})}/>)}
I can generate all the needed metadata (above for simplicity just fieldNames: string[]) easily. I can do even better with
{fieldsMetadata.map(f => <Edit2 entity={entity} field={f}/>)}
where Edit2 is a "smarter" component. What bothers me is that all my edits get re-rendered on any change. This may be a premature optimization, but given that I want to use this component many time on many pages, it may be a valid concern.
So I'm asking whether it's possible to write such a component without it re-rendering every time or whether there's something performance-relevant what I'm missing?
The problem in with your component re-rendering is because on passing the inline arrow function which is created again on every render
Once you avoid it you can implement each Edit component with React.memo or PureComponent depending on whether you implement it as a functional component or class
const onChange= useCallback((name, value) => {
setEntity(prev => ({...prev, [name]: value}));
}, []);
...
{fieldNames.map(f => <Edit name={f} value={entity[f]}
onChange={onChange}/>)}
and Edit component would be like
const Edit = (props) => {
const {value, name, setEntity} = props;
const handleChange = (e) => {
const newValue = e.target.value;
onChange(name, newValue);
}
return <input value={value} name={name} onChange={handleChange} />
}
export default React.memo(Edit)
P.S Make sure you are merging the state correctly with setEntity

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

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]);

Resources