useState
worksstate
prevState
in a function to change the state
In this article we’ll figure out what the useState
is and the rules to follow for its successful use.
useState
is a React
hook for managing components rendering. The hook can be used inside each component which needs to be updated and re-rendered according to its state changes.
useState
hook is a method that accepts just one argument which is an initial state.
This argument could be:
useState
returns an array of two elements:
Using useState
hook we're able to divide logic into several states and each of it will be applied for proper element inside the render
Keep in mind that hooks have some particular rules of their use:
WRONG ourState = newValue
.
RIGHT changeStateValue(newValue)
.
Let’s take a look at the basic use of state when performing conditional rendering:
import React from 'react'
const StateExample = () => {
const [isElementVisible, changeElementVisibility] = React.useState(true)
const handleHideElement = () => {
changeElementVisibility(false)
}
return (
<div>
{isElementVisible && <div>Element that should be hidden on button click</div>}
<button onClick={handleHideElement}>Hilde element above</button>
</div>
)
}
export default StateExample
Let's see what we’ve done: when doing destructuring assignment, the isElementVisible
constant was assigned the initial state, and the changeElementVisibility
constant got a function for updating the state.
How constants and functions should be properly named? The right name should briefly describe the purpose and the role that the constant or function will play in a particular component. It might seem too obvious at the first sight. But this recommendation is often neglected by the developers. First things first, by doing this you ensure a rapid navigation across your entire project. In case the whole team is engaged for the project, the given name defines how quickly other developers can navigate through your code and continue working with it. Think of other people and be one step ahead. To get the right idea, try to always visualize yourself being completely new to this project or working in the pair.
To update component state it is vital to know that:
You SHOULD AVOID to use anonymous function inside onClick and make changes directly out of there. The anonymous function will be initialized anew each time your page will render. Its affect the entire application performance.
You SHOULD USE a predefined function to describe the state change, in our case handleHideElement. For example, if an element would not be hidden only by clicking on one button, but possibly somewhere else - we can easily reuse the handleHideElement function, reducing the amount of code and improving readability.
In our example, by clicking on the button, handleHideElement
is called and the value is passed to changeElementVisibility
. Thus, the state changes and the component is being re-rendered, hiding therefore the element we need.
import React from 'react'
const StateExample = () => {
const [isElementVisible, changeElementVisibility] = React.useState(true)
// Correct usage
const handleHideElement = () => {
changeElementVisibility(false)
}
return (
<div>
{isElementVisible && <div>Element that should be hidden on button click</div>}
{/* Don't do that */}
<button onClick={() => changeElementVisibility(false)}>Hilde element above</button>
{/* Correct usage */}
<button onClick={handleHideElement}>Hilde element above</button>
</div>
)
}
export default StateExample
By the way, when performing conditional rendering, it is correct to use a construction with a logical AND
(&&
). If the left side of the condition (isElementVisible
) returns false
, React will ignore this element in the render. In case the left side returns true
- the construct returns the element that React will draw in the browser.
Working with classes you’re able to pass a callback function as the second argument to the setState
method and this callback
function would be fired as soon as state has been changed. However, using the useState
hook we’re not able to do that. To reach the goal, you should apply the useEffect
method by adding the dependency isElementVisible
as the second argument. Thus, the function will be called each time the component state changes.
Be aware that
useEffect
method will be called at the very first component rendering and make sure you take this into account when building logic.
import React from 'react'
const StateExample = () => {
const [isElementVisible, changeElementVisibility] = React.useState(true)
React.useEffect(() => {
// function will be called on each "isElementVisible" change
}, [isElementVisible])
const handleHideElement = () => {
changeElementVisibility(false)
}
return (
<div>
{isElementVisible && <div>Element that should be hidden on button click</div>}
<button onClick={handleHideElement}>Hilde element above</button>
</div>
)
}
export default StateExample
As you can see in the example above, the function that updates the state takes an updated value as an argument. Yet this is not all. It can also take a function that returns an updated value. The argument of this function is the current state until the next update takes place.
The example below shows the function used as an argument to update the state of input fields.
import React from 'react'
// local variables
const FIELD_NAMES = {
FIRST_NAME: 'firstName',
LAST_NAME: 'lastName'
}
const StateExample = () => {
const [formValues, changeFormValues] = React.useState({
[FIELD_NAMES.FIRST_NAME]: '',
[FIELD_NAMES.LAST_NAME]: ''
})
const handleInputChange = fieldName => e => {
const fieldValue = e.target.value
changeFormValues(prevState => ({
...prevState,
[fieldName]: fieldValue
}))
}
return (
<div>
<input
type='text'
onChange={handleInputChange(FIELD_NAMES.FIRST_NAME)}
name={FIELD_NAMES.FIRST_NAME}
value={formValues[FIELD_NAMES.FIRST_NAME]}
/>
<input
type='text'
onChange={handleInputChange(FIELD_NAMES.LAST_NAME)}
name={FIELD_NAMES.LAST_NAME}
value={formValues[FIELD_NAMES.LAST_NAME]}
/>
</div>
)
}
export default StateExample
To update the state of a specific element by its name, use a higher order function and pass the names of the inputs across the closure before the event handler in the input is called. An anonymous function should be passed to changeFormValues
method for changing input's state. This anonymous function returns a NEW state object based on the PREVIOUS one, but with an updated field.
You might have noticed that I’ve created the
FIELD_NAMES
variable and defined an object for it in which I described the names of the inputs to use them inside the component. Try to remember and make use of this approach. If you’ll have to change the name of the input, this could be easily done it once and in one place. I guess you can admit that this is much more convenient than changing the name in each input separately. This approach can be defined asENUM
.
Now having a full set of tools and using the state of the components correctly, you can easily implement complex logic for your application.