How to get updated React state after being set? - javascript

I have a form that has all text elements. How come when I use this change handler function and set the state using the change event listener, it logs what the state was before the change?
const handleChange = event => {
const { name, value } = event.target
setSomeState(prevDateInputs => ({
...prevStateInputs,
[name]: value,
}))
console.log(someState) // ← this logs the value of the state before it was changed
}

In the recent react rfcs release you can use 'use' same as javascript async await method. more details can be found in this link https://github.com/reactjs/rfcs/pull/229

Is it essential to use setState? Could useRef work?
import {useRef} from 'react';
const App = () => {
const inputRef = useRef(null);
function handleChange() {
const {name, value} = inputRef.current;
console.log({[name] : value});
}
return (
<div>
<input
onChange={handleChange}
ref={inputRef}
type="text"
id="message"
name="message"
/>
</div>
);
};
export default App;
But assuming your real use case doesn't involve a console.log, it may not matter if setState doesn't update instantly. In the below example We see the new value displayed on the screen near instantly in the h2 tag even if the console.log shows the old value:
import {useState} from 'react';
const App = () => {
const [message, setMessage] = useState('');
const [someState, setSomeState] = useState('');
const handleChange = event => {
const { name, value } = event.target
setMessage(value)
setSomeState({[name]:value})
console.log('value is:', someState);
};
return (
<div>
<input
type="text"
id="message"
name="message"
onChange={handleChange}
value={message}
/>
<h2>{JSON.stringify(someState)}</h2>
</div>
);
};
export default App;
Some more details here.

Related

How to write value to localStorage and display it in input on reload?

I have an input on the page, initially it is empty. I need to implement the following functionality: on page load, the component App fetches from localStorage a value of key appData and puts it in the input. That is, so that in the localStorage I write the value to the input and when reloading it is displayed in the input. How can i do this?
I need to use useEffect
import { useEffect, useState } from "react";
export default function App() {
const [userData, setUserData] = useState("");
useEffect(() => {
localStorage.setItem("Userdata", JSON.stringify(userData));
}, [userData]);
return (
<div>
<input value={userData} onChange={(e) => setUserData(e.target.value)}></input>
</div>
);
}
Use the change event to write to the localStorage, then use an init function in the useState hook.
import { useState } from 'react';
const loadUserData = () => localStorage.getItem('UserData') || '';
const saveUserData = (userData) => localStorage.setItem('UserData', userData);
export default const Application = () => {
const [ userData, setUserData ] = useState(loadUserData);
const handleUserDataUpdate = e => {
const userData = e.target.value;
setUserData(userData);
saveUserData(userData);
};
return <div>
<label htmlFor="testInput">Test Input</label>
<input id="testInput" value={ userData } onChange={ handleUserDataUpdate } />
</div>;
}
If you need an example using uncontrolled inputs, here is one using useEffect :
import { useEffect } from 'react';
const loadUserData = () => localStorage.getItem('UserData') || '';
const saveUserData = (userData) => localStorage.setItem('UserData', userData);
export default const Application = () => {
const inputRef = useRef();
useEffect(() => {
inputRef.current.value = loadUserData();
}, []); // initial load
const handleUpdateUserData = () => {
saveUserData(inputRef.current.value);
};
return <div>
<label htmlFor="testInput">Test Input</label>
<input ref={ inputRef } id="testInput" onChange={ handleUpdateUserData } />
</div>;
}
You can set a default value for the input inside state.
const [userData, setUserData] =
useState(JSON.parse(localStorage.getItem('Userdata')) || '');
So when the component mounts (after reload), the initial userData value is taken directly from the localStorage. If it's empty, the fallback value will be set ('').
Note: Make sure to add also the onChange handler to the input.

how to get current value from input field in react js

I am trying to get current value from input field, but after onclick I am getting preious value in colsole.
here is my code
import { React, useState } from "react";
const CompoundIntrest = () => {
const [capitalValue, setcapitalValue] = useState(1000);
const ChangeCapital = () => {
setcapitalValue(capitalValue - 100);
};
const Calculate = () => {
console.log(capitalValue);
};
return (
<>
<button
onClick={() => {
ChangeCapital();
Calculate();
}}
>
click
</button>
<input type="number" value={capitalValue} />
</>
);
};
export default CompoundIntrest;
State updates occur asynchronously, so you won't have the updated state value inside the event handler.
You can lift the new value i.e. capitalValue - 100 to a scope from where it can be passed down to both ChangeCapital & Calculate.
const CompoundIntrest = () => {
const [capitalValue, setCapitalValue] = React.useState(1000);
const handleClick = () => {
const newCapitalValue = capitalValue - 100;
ChangeCapital(newCapitalValue);
Calculate(newCapitalValue);
};
const ChangeCapital = (capitalValue) => {
setCapitalValue(capitalValue);
};
const Calculate = (capitalValue) => {
console.log(capitalValue);
};
return (
<React.Fragment>
<button onClick={handleClick}>click</button>
<input
type="number"
value={capitalValue}
onChange={(e) => setCapitalValue(e.target.value)}
/>
</React.Fragment>
);
};
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<CompoundIntrest />);
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div id="root"></div>
Note: The state updater function is called synchronously but the state updates happen asynchronously.
This becomes more clear if you update the state by passing a state updater callback, you would see that the callback is fired synchronously. Notice the order of logs in the example below:
function App() {
const [count, setCount] = React.useState(0);
const handleClick = () => {
console.log("Before calling setCount");
setCount((currCount) => {
console.log("Inside setCount");
return currCount + 1;
});
console.log("After calling setCount");
};
return <button onClick={handleClick}>Count: {count}</button>;
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div id="root"></div>
You can use Use useEffect Like this:-
import React,{useState,useEffect} from "react";
const CompoundIntrest = () => {
const [capitalValue, setcapitalValue] = useState(1000);
const ChangeCapital = () => {
setcapitalValue(capitalValue - 100);
};
const Calculate = () => {
console.log(capitalValue);
};
useEffect(()=>{
console.log("afet chage",capitalValue);
},[capitalValue]);
return (
<>
<button
onClick={() => {
ChangeCapital();
Calculate();
}}
>
click
</button>
<input type="number" value={capitalValue} />
</>
);
};
You can use the onChange event in the input field to get current value.
const [currentValue, setCurrentValue] = useState['']
const changeHandler = (e:any) => {
e.preventDefault();
const { value } = e.target
console.log('value', value);
setCurrentValue(value)
}
<input type="string" value={currentValue} onChange={(e:any) => changeHandler(e)}/>
I think, you should add onChange method in input tag like below:
Then you get current value in onClick event in button tag.
import { React, useState } from "react";
const CompoundIntrest = () => {
const [capitalValue, setcapitalValue] = useState(1000);
const ChangeCapital = () => {
setcapitalValue(capitalValue - 100);
};
useEffect(() => {
const Calculate = () => {
console.log(capitalValue);
};
Calculate()
}, [capitalValue])
return (
<>
<button
onClick={() => {
ChangeCapital();
}}
>
click
</button>
<input type="number" value={capitalValue} onChange={(e) => setcapitalValue(e.target.value)} />
</>
);
};
export default CompoundIntrest;
In the case of controlled component, other members have already provided the answer, I just want to give you an idea about uncontrolled component.
Assuming that we are dealing with an uncontrolled component ( "input" element ) then how we can get the value.
1. import { React, useState, useRef, useEffect } from "react";
2.
3. const CompoundIntrest = () => {
4. const [capitalValue, setcapitalValue] = useState(1000);
5. const inputRef = useRef(null);
6.
7. useEffect(() => {
8. console.log(capitalValue);
9. }, [capitalValue]);
10.
11. const ChangeCapital = () => {
12. setcapitalValue(inputRef.current.value - 100);
13. };
14.
15. return (
16. <>
17. <button onClick={ChangeCapital}>click</button>
18. <input ref={inputRef} type="number" />
19. </>
20. );
21. };
22.
23. export default CompoundIntrest;
At line 5, we have created a ref with initial value null using useRef hook of react, which later will be used to store reference of input element.
At line 18, we have assigned the inputRef to the ref of input element, which will be use to get the value from the field.
At line 12, we are getting the value of input as inputRef.current.value .
To check the update in the value of capitalValue state onClick event of button we can use useEffect hook of react ( From Line 7 to Line 9 is doing the same ).
PS : Please let me know if this clear your doubt or not. Thanks for reading the answer.
your code is fine, and your state is successfuly updated, the problem is the timing of calling your console. react handles your code async, it means it starts your changeCapital, and before the change capital function is finished it calls the calculate function, so the value of your state, is the previous value.
you need to call your calculate function somewhere else:
you can call it in a UseEffect hook, this way your function gets called whenever your state has successfuly changed, or
you can call your calculate in 'onchange' event of your input feild
if you want the better solution, the first one is more reactly than the second one

react state is not updating the UI

I have a Form Component where it contains a state that should be updated (on input change) and it looks like this:
import { useState } from 'react';
export const Test = () => {
const [state, setState] = useState({
name: 'khaled',
age: 18
})
const handleInputChange = (e) => {
let stateCopy = state
for(let key in stateCopy) {
if(key === 'name') {
stateCopy[key] = e.target.value;
}
}
setState(stateCopy);
}
return(
<div>
<span>Name</span>
<input onChange={ handleInputChange } />
<span>{state.name}</span>
</div>
)
}
and it imported in the app component
import { Test } from '../../components/Test';
function App() {
return (
<Test />
);
}
export default App;
and whenever i try to change the name inout it not update the ui
To make the input a controlled component, both value and onChange props should be assigned.
<input value={state.name} onChange={handleInputChange} />
handleInputChange function can be improved to make sure that the state is updated immutably:
const handleInputChange = ({ target: { value } }) => {
setState(prevState => ({...prevState, name: value}));
}
This does not work because your "stateCopy" object isn't actually a copy, its the actual state object. you are setting the state to the same object which causes react to think the state didn't change at all.
instead you should copy the state like this
const handleInputChange = (e) => {
let stateCopy = {...state}
state.name = e.target.value
setState(stateCopy);
}
You should also note that unless there is a good reason for your choice of state in my opinion you should use a seperate useState for each element in the state which results in the much simpler
import { useState } from 'react';
export const Test = () => {
const [name, setName] = useState('khalad')
const [age, setAge] = useState(18)
const handleInputChange = (e) => {
setName(e.target.value)
}
return(
<div>
<span>Name</span>
<input onChange={ handleInputChange } />
<span>{state.name}</span>
</div>
)
}
simply do it like this, it will work
const handleInputChange = (e) => {
setState({...state, name: e.target.value})
}

How to change TexField variant on input focus and maintain the focus using Material-ui

When the user focuses on input I want to change the variant on the TextField. Bellow snippet does that, but the input loses focus. you need to click again on input in order to focus and write some text inside
import React, { useState } from 'react'
import { TextField } from '#material-ui/core'
const App = () => {
const [name, setName] = useState('')
const [focus, setFocus] = useState(false)
return <TextField
variant={focus ? 'outlined' : 'standard'}
onFocus={(e) => setFocus(true)}
value={name}
name='name'
onChange={(e) => setName(e.target.value)} />
}
sandbox: https://codesandbox.io/s/material-ui-dynamic-variant-7up6q?file=/demo.tsx
My understanding is the following:
TextField component re-renders with new props and is creating a new input element to display while destroying the old one. In this way, the user needs to do 2 clicks on input before texting.
I tried with onClick also, leading to the same result.
Is there a way to obtain these results without losing the focus on input?
Use "useRef" for focusing the input, inputRef prop will help you to set ref. And useEffect to track and update.
const App = () => {
const [name, setName] = useState("");
const [focus, setFocus] = useState(false);
const inputRef = useRef(null);
const onFocus = () => {
setFocus(true);
};
const onBlur = () => {
setFocus(false);
};
useEffect(() => {
if (focus) {
inputRef.current.focus();
}
}, [focus]);
return (
<TextField
variant={focus ? "outlined" : "standard"}
onFocus={onFocus}
onBlur={onBlur}
value={name}
inputRef={inputRef}
name="name"
onChange={(e) => setName(e.target.value)}
/>
);
};
export default App;
You can check demo here:
Link - https://codesandbox.io/s/material-ui-dynamic-variant-forked-8pbdi?file=/demo.tsx

The state is not updated via the useState hook the first time

The state is updated only on the next keystroke but with the previous state. Screen 1
When you click on updateForm (), it is also empty, only after the second click, the state is updated. Screen 2
I understand that this is due to asynchrony, but in this case I do not know how to use it.
Home.jsx
import React, { useState } from 'react';
import { Form } from '../components/Form/Form';
const Home = () => {
const [dateForm, setDataForm] = useState({});
const updateForm = eachEnry => {
setDataForm(eachEnry);
console.log(dateForm);
};
return (
<div>
<Form updateForm={updateForm} />
</div>
);
};
export default Home;
Form.jsx
import React, { useState } from 'react';
import './Form.scss';
export const Form = ({ updateForm }) => {
const initInputState = {
name: '',
password: ''
};
const [dataForm, setDataForm] = useState(initInputState);
const { name, password } = dataForm;
const onChange = e => {
setDataForm({
...dataForm,
[e.target.name]: e.target.value
});
};
const onSubmit = e => {
e.preventDefault();
updateForm(dataForm);
};
return (
<div>
<form onSubmit={onSubmit}>
<input
type="text"
placeholder="Name"
value={name}
onChange={onChange}
name="name"
/>
<input
placeholder="Password"
onChange={onChange}
value={password}
type="text"
name="password"
/>
<button type="submit">Login</button>
</form>
</div>
);
};
Your code is working fine. You just doing console.log before the state is updated. State updates happen not when you using an update state function. It's happening when all component action and nested components actions are done.
Check your code with console log on another place click to check
As you can see I placed a console log on every Home component rerender. You can check that all works fine.
P.S. I did some improvements to your code. Check if u like it. And add a comment to updateForm function. Check this one too, please.
You evidently are not setting your state properly, here
setDataForm({
...dataForm,
[e.target.name]: e.target.value
});
should be
setDataForm(c => ({
...c,
[e.target.name]: e.target.value
}));

Categories

Resources