One of the most interesting features developed by React team is React hooks. React Hooks provide an easy and direct way to work with React function components, easier than using class component lifecycle methods. Before the introduction of the concept of React hooks, React developers had to strictly use class components where states were involved, thanks to React team, upcoming developers do not have to worry about all those lifecycle methods. In this article, we will briefly discuss the useState
hook and how it's used to initialize and update states of different data types.
React hooks come with react as named exports, the only thing we need to do in order to use react hooks is to import them into our component. For example in our case study, we could import useState
hook using the following line of code.
`import { useState } from 'react'`
useState
hook is used to update the values of state variables in functional components. Do not be mistaken, React Hooks cannot be used in class components.
Before we can use any of the useState
hook, we have to declare and initialize it with an initial value. The initial value should reflect the data type we expect the state to store. For example, if we want to declare a state for a number, we can do so using the following line of code.
`const [age, setAge] = useState(18)` *number type*
`const [order, setOrder] = useSate({})` *order declared as object*
`const [orders, setOrders] = useState([])` *orders declared as array*
The above line of code declares a state called age with an initial value of 18. This means that if the state value is not updated in the future, its value will always be 18. You are now wondering what the rest of the declaration means. Well, here we go: useState
hook is a method that returns an array containing a variable to store the value of the state and a setter method to update the variable. We could store the return value in a variable named something else but it is more readable this way. This syntax is called Array Destructuring Assignment
const StateHook0 = () => {
const [age, setAge] = useState(18)
return (
<div>
<h1>{age}</h1>
</div>
)
}
In order to update the value of age, we use the setter method and provide the new value in parenthesis as an argument.
`setAge(23)` changes the value of age from 18 to 23. If you try to display the value of age after this update, 23 will be displayed in place of 18.
const StateHook0 = () => {
const [age, setAge] = useState(18)
return (
<div>
<input type="text" value={age}
onChange={(e) => setAge(e.target.value)}/>
<h1>{age}</h1>
</div>
)
}
)
}
The most interesting thing about this hook is that calling its setter method causes re-rendering, this is how your new age gets displayed on the DOM without much effort. Every time you call setAge(value)
method, the new value will always be displayed on the browser.
Updating states dependent on the previous state
The way we have updated age
above is only safely applicable in cases when we are updating a state that does not need to depend on its previous values. In order to safely update a state that is dependent on its previous value successfully, we use an arrow function to return the final value of the state in the setter method. Here in an example in which we need to keep track of the value of count
.
const StateHook1 = () => {
const [count, setCount] = useState(0)
/**
* Updating state that is dependent on the previous state
* Use arrow function to return updated value in setCount
*/
return (
<div>
<button style={{padding: '1rem'}}
//Use arrow function to set state dependent on a previous state
onClick={() => setCount((count) => count + 1)}
>Counting {count}</button>
</div>
)
Updating states of Object
type
An attempt to update values of object properties will not always work as expected if the properties are updated independently without considering the rest of the properties. In this case we use Object destructuring syntax as in the example below
const StateHook2 = () => {
// Declaring states for objects
const [name, setName] = useState({firstName:'', lastName:'' })
return (
<div>
<form action="">
<input type="text"
// Use Spread operator to update object states,
// use state does not merge the objects properties automatically during update
onChange={(e) => setName( {...name, firstName: e.target.value})} value={name.firstName} />
<input type="text"
onChange={(e) => setName( {...name, lastName: e.target.value})} value={name.lastName} />
<h1>FirstName: {name.firstName}</h1>
<h1>LastName: {name.lastName}</h1>
</form>
</div>
)
}
Updating states declared as an array
You have already guessed it right, yes, it is the famous Array destructuring again. We use array destructuring syntax to update states of the array type. The following code snippet will help you understand it better.
const StateHook3 = () => {
/**
* Updating array states
* Use Spread operator
*/
const [users, setUsers] = useState([]) //Initialize as empty array
//User name state
const [user, setUser] = useState('')
return (
<div>
{/* Getting users' names from user input */}
<form action="" onSubmit={(e) =>{
e.preventDefault()
//Use spread operator to update arrays states that depend on preveus array
setUsers([...users, user])
}
}>
<input type="text" value={user} onChange={(e) => setUser(e.target.value)}/>
<input type="submit" value="Submit" />
</form>
{
users.map((user, index) => <li key={index}>{user}</li>)
}
</div>
)
}
Lastly, useState
hook makes updating and re-rendering states seamless and easy. One most important thingS to remember is that states can only be declared in the global scope of a functional component. This means states cannot be declared inside loops, branching statements, or nested functions of a component. To read more about useState
hook, don't miss the documentation
Top comments (0)