DEV Community

Cover image for React Hooks: Intuitive Introduction to useState Hook
Felix Owino
Felix Owino

Posted on

React Hooks: Intuitive Introduction to useState Hook

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'`
Enter fullscreen mode Exit fullscreen mode

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*
Enter fullscreen mode Exit fullscreen mode

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode
const StateHook0 = () => {
    const [age, setAge] = useState(18)

  return (
    <div>
        <input type="text" value={age}  
        onChange={(e) => setAge(e.target.value)}/>

        <h1>{age}</h1>
    </div>
  )
}
  )
}
Enter fullscreen mode Exit fullscreen mode

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>
  )
Enter fullscreen mode Exit fullscreen mode

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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)