How does useRef() cause a view change without a re-render? - javascript

So, I have this basic code below.
In the code when the user clicks on the button, it focuses on the input field and types in the word "test".
How does this happen since the state is never changed? i.e since a "re-render" is not occurring- how does the view change?
import { useRef } from "react";
const App = () => {
const inputRef = useRef(null);
const handleClick = () => {
inputRef.current.focus();
inputRef.current.value = "test";
};
return (
<>
<input ref={inputRef}></input>
<button onClick={handleClick}>Click me!!</button>
</>
);
};

Related

How state Varibales in React JS works without using it on UI

Why my react functional component is rendered only 2 times when I click "Click me" button more than two times, if I have used a state variable and a change to that state variable inside that component, "but haven't used that state variable anywhere inside the UI"?
code:
const Header = () => {
const [title, setTitle] = useState("My Title");
console.log('rendered');
return (
<>
<button onClick={e => {
setTitle("Title My");
}}>Click me</button>
</>
);
};
export default Header;
In this case, the component is only rendered two times because React is optimized for performance, and it will only re-render a component if its state or props have changed.
If you want the component to re-render each time the button is clicked, you have to set the state with a different value, not always the same (Title my in your case)
I suspect React doesn't see any reason to re-render because state hasn't changed; the value is the same as the last time. But if you want to re-render you can forceRender using the following code.
import { useState } from 'react'
function useForceUpdate(){
const [value, setValue] = useState(0);
return () => setValue(value => value + 1);
}
const Header = () => {
const forceUpdate = useForceUpdate();
const [title, setTitle] = useState("My Title");
console.log('rendered');
return (
<>
<button onClick={e => {
setTitle("Title My");
forceUpdate();
}}>Click me</button>
</>
);
};
export default Header;

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

Why KeyboardEvent isn't working with this Input element in react?

I'm working with controlled input elements at work and I'm stuck.
Basically, I need to autofill some input elements in a form, but the problem is that I need to fill it in a way that simulates the user input (in this case, typing) in order to trigger the onChange function's logic. So, because of that. I need to emulate the typing behavior and not just set the value for the element.
Despite having searched for previous questions and reading docs about KeyboardEvent, I haven't been able to make this work.
Currently, I'm experimenting in a Codesandbox just for making things easier, but even with this simple environment, I can't manage to get this to work.
Here's the code and its Codesandbox link
import { useRef, useState, useEffect } from "react";
import "./styles.css";
export default function App() {
const [state, setState] = useState();
const inputRef = useRef();
const event = new KeyboardEvent("keypress", { key: 99 });
useEffect(() => {
inputRef.current.dispatchEvent(event);
}, [inputRef]);
const onChange = (e) => {
setState(e.target.value);
};
return (
<div className="App">
<h1>{state}</h1>
<input
type="text"
id="name"
onChange={onChange}
ref={inputRef}
value={state}
/>
</div>
);
}
Hopefully one of you guys could give me a hand with this.
Thanks for reading!
Related to the comments:
I think that it shouldn't be necessary to be dispatching a keypress event to get your special effect logic to run.
For example, you can use a useEffect which just runs on initial render to trigger whatever special logic you want -- and this way you can just have a regular initial value for the form state.
import { useState, useEffect } from "react";
import "./styles.css";
export default function App() {
// In the useState call, you can initialize the value.
const [state, setState] = useState("initial value");
const specialEffectFunction = () => {
// here's the code for the special effect you want to run on load
console.log('this is the special onChange effect')
}
useEffect(() => {
// This will trigger the special function which you want to run
// when the app loads
specialEffectFunction();
// if it really HAS to be the `onChange` function that's called,
// then you'll need to call that with a fake ChangeEvent.. but I don't
// think that should be necessary...
}, [])
const onChange = (e) => {
setState(e.target.value);
};
return (
<div className="App">
<h1>{state}</h1>
<input
type="text"
id="name"
onChange={onChange}
value={state}
/>
</div>
);
}
I couldn't fix the problem with Keyboard Event for my lack of knowledge about it, but I hope I managed to solve the problem of emulating a human autofill the input using the below code.
function AutoFillInput({ finalValue }: { finalValue: string }) {
const [inputValue, setInputValue] = useState('');
const [sliceStart, setSliceStart] = useState(0);
const changeHandler = useCallback((event) => {
setInputValue(event.target.value);
}, []);
useEffect(function handleFinalValueChange() {
setInputValue('');
if (sliceStart < finalValue.length)
setSliceStart(x => x + 1);
}, [finalValue]);
useEffect(function handleSlice() {
setInputValue(finalValue.slice(0, sliceStart));
if (sliceStart < finalValue.length) {
setTimeout(() => {
setSliceStart(x => x + 1);
}, 800);
}
}, [sliceStart]);
return (
<input
value={inputValue}
onChange={changeHandler}
placeholder={'Auto fill input'}
/>
)
}
function App() {
return (
<div >
<AutoFillInput finalValue={'hello world'} />
</div>
);
}
export default App;

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

Categories

Resources