This content originally appeared on DEV Community and was authored by keyvan
we have two options when we are dealing with inputs in react realm:
controlled components :
we update the value of the input by usingvalue
prop andonChange
eventuncontrolled component :
DOM takes care of updating the input value. we can access the value by setting aref
on the input
There is a chance that you have encountered a situation that whenever you type something into an input or textarea there is a delay (lagging) and the input update is very slow. It is rather annoying and a bad user experience.
This behavior is a side effect of using controlled components. let's see why and how we can mitigate the issue
underlying cause
In controlled components, there is a cycle an input goes through.on every keystroke, we change some state(it could be in a global state like Redux or by useState
hook), and React re-renders and set the input's value prop with the new state. This cycle could be expensive. That's why we face a delay while updating the input. another situation would be having a huge component that every keystroke causes the component to re-render.
examples:
there is a complex component (e.g., a big form with lots of inputs), and whenever the input changes, the whole component re-renders
a big web app with state management (e.g., redux, context) that on every keystroke changes something in the store that triggers a re-render of the whole app
bounce, debounce might work?
if we bounce updating the global state and getting back the same value would add a delay making the input much worse. although it would be great to use it with isolated component.bounceing and debouncing is effective whenever we want to call an API and we don't want to fetch loads of information on every keystroke.
solutions
there are a couple of ways that we could address this issue.
Change to uncontrolled component
let's assume we have a component with a couple of inputs :
function ComponentA() {
const [value1, setState1] = useState();
const [value2, setState2] = useState();
const [value3, setState3] = useState();
const handleSubmit = () => {
//do something
};
<form onSubmit={handleSumbit}>
<input value={value1} onChange={e => setState1(e.target.value)} />;
<input value={value2} onChange={e => setState2(e.target.value)} />
<input value={value3} onChange={e => setState2(e.target.value)} />
</form>;
}
let's assume we have a component with a couple of inputs. we can change the code to use the uncontrolled component then input doesn't need to go through the re-rendering phase to get the value back.
function ComponentB() {
const input1 = useRef();
const input2 = useRef();
const input3 = useRef();
const handleSubmit = () => {
// let value1=input1.current.value
// let value2=input2.current.value
// let value3=input3.current.value
// do something with them or update a store
};
return (
<form onSubmit={handleSubmit}>
<input ref={input1} />;
<input ref={input2} />
<input ref={input3} />
</form>
);
}
onBlur
we can update our state (or global state) with the onBlur event. although it is not ideal in terms of user experience
onInputBlur = (e) => {
//setting the parent component state
setPageValue(e.target.value);
}
onInputChange = (e) => {
/*setting the current component state separately so that it will
not lag anyway*/
setState({inputValue: e.target.value});
}
return (
<input
value = {this.state.inputValue}
onBlur = {this.onInputBlur}
onChange={this.onInputChange}
>
)
Isolated component
the optimal solution is to use an isolated input component and manage the input state locally
import { debounce } from 'lodash';
function ControlledInput({ onUpdate }) {
const [value, setState] = useState();
const handleChange = e => {
setState(e.target.value);
onUpdate(e.target.value);
};
return <input value={value} onChange={handleChange} />;
}
function ComponentB() {
const input1 = useRef();
const input2 = useRef();
const input3 = useRef();
const handleSubmit = () => {
//do something with the values
};
return (
<form onSubmit={handleSubmit}>
<ControlledInput
onUpdate={val => {
input1.current = val;
// update global state by debounce ,...
}}
/>
;
<ControlledInput
onUpdate={val => {
input1.current = val;
// update global state by debounce ,...
}}
/>
;
<ControlledInput
onUpdate={val => {
input1.current = val;
//update global state by debounce ,...
}}
/>
;
</form>
);
}
we have the benefit of having a controlled component and not causing any unnecessary re-renders or going through an expensive one. we can make custom components that check for certain criteria and show success or error messages. now we can implement a bouncing, debouncing mechanism and update the global state or fetch an API. our input speed is natural and we wouldn't cause any unnecessary update or API calling on every keystroke.
I'd be happy to hear from you, let's connect on Twitter
This content originally appeared on DEV Community and was authored by keyvan
keyvan | Sciencx (2022-03-23T08:53:20+00:00) How to solve input delay (lagging) in react. Retrieved from https://www.scien.cx/2022/03/23/how-to-solve-input-delay-lagging-in-react/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.