DISCLAIMER: i’m new at React, and just trying to build good habits.
So, the example would be a simple contacts app, where you type in names, and all the names are rendered below the input field. pic.1 simple contacts app mechanics
First, and the most intuitive way, of making this work is using useState()
hook with our input field. It’s also widely suggested in tutorials around the web.
method 1 with useState()
:
const [inputState, setInputState] = useState('');
const [people, setPeople] = useState([]);
const onChange = (event) => {
setInputState(event.target.value)
}
const onSubmit = (e) => {
e.preventDefault()
setPeople([...people, inputState])
setInputState('') // pay extra attention to this line
}
// ...
<form onSubmit={onSubmit}>
<input onChange={onChange} value={inputState} />
</form>
//...
Looks simple enough. We have inputState
, that is up-to-date with input field because of onChange
function. However (!!!), in this video about common react-hook mistakes, Kyle points, that this method isn’t the optimal one, cause it makes the whole page re-render every time we change input value. So, he suggests using useRef()
hook instead.
*method 2 with useRef()
:*
const [people, setPeople] = useState([]);
const inputRef = useRef([]);
const onSubmit = (e) => {
e.preventDefault()
setPeople([...people, inputRef.current.value])
// inputRef.current.value = '' <== won't work
}
// ...
<form onSubmit={onSubmit}>
<input ref={inputRef} />
</form>
//...
The second method works fine, and re-renders the page only once, when the submit event happens. However, we are going to submit this form multiple times, so we want onSubmit
function to clear the input value. If we try adding inputRef.current.value = '';
in the end of onSubmit
function, it breaks our app. The reason must be that setting state is asynchronous, so it ends up with updating state after making input value empty. To resolve it we can use another hook, useEffect()
, which will clear our input value only after our stateful element is updated
method 2 updated with useEffect()
:
const [people, setPeople] = useState([]);
const inputRef = useRef([]);
useEffect(() => inputRef.current.value = '', [people])
const onSubmit = (e) => {
e.preventDefault()
setPeople([...people, inputRef.current.value])
}
// ...
<form onSubmit={onSubmit}>
<input ref={inputRef} />
</form>
//...
Finally. What’s the question??
We have these two methods:
-
First one is intuitive, using simple mechanics, using just one hook, BUT re-rendering the page multiple times.
-
Second one uses as much as three different hooks, more complex mechanics, BUT re-renders only on submit.
Which should I use? Which could be considered the correct one? Or maybe I’m missing something and the answer is ‘neither’?
Some similar questions here discuss the differences, but as we are already aware of differences, what to choose?
If your form doesn’t have validation, you can use
useRef
.
But should I? Does this stack of hooks really improve the performance?
I’d appreciate your thoughts and suggestions!
3
Answers
The choice between the two methods depends on your specific use case and your priorities for your application.
If re-rendering the page multiple times is not a performance concern for your application and you prefer a simpler, more intuitive code, then the first method may be a good choice.
On the other hand, if you want to optimize performance and reduce unnecessary re-renders, then the second method may be a better choice, even though it is more complex.
It’s also worth noting that the second method may be more suitable for larger and more complex forms that have multiple input fields and complex validation rules, while the first method may be sufficient for smaller and simpler forms.
Ultimately, there is no one "correct" method, as both have their advantages and disadvantages. You should choose the method that best fits your specific use case and your priorities for your application, whether it be simplicity, performance, or scalability.
I haven’t watched the video, but if the author suggests to use
useRef()
instead of state to save performance, he doesn’t seem to have much clue of how React actually works.React only applies changes to the DOM when it spots the changes in the virtual DOM, which is the representation of the nodes used to keep track of necessary changes. If your state change causes many DOM updates, it means that you do it wrong, most likely recreate a state value unnecessarily. The easiest way to this mistake is to recreate object in state, instead of memoizing it.
Using states means achieving much cleaner and reusable React code. Use
useRef()
only when you need to perform an action on a DOM element (likefocus()
).We have had issues with performance on large forms (forms with lots of inputs) and using state. While a simple state update is not much of a performance hit, if there are fields that update depending on that state, there may be a large performance hit, as there was in our case.
Our solution was to move to
react hook form
. While this may not answer your question directly, this is the direction we ended up going to solve issues that using state on the form caused and not to go down the slightly messyuseRef
path.You also get some great methods like
isDirty
andisValid
to help in form submission. You can also clear the form relatively easy with thereset
method.