#9 Introduction to Hooks - Delightful React

Subscribe to my newsletter and never miss my upcoming articles

WMRSG.png

We are finally ready to level up our components. Until now, all our components did was show a few elements. It’s time to to create truly powerful components that can hold internal state, run side-effects and do so much more. Ready? Let’s do this!


*Note: This article is a part of the Delightful React Series, as part of which, I am releasing one chapter every day for 25 days. Please support me in anyway you can! *


Theory time

Pure vs Impure functions

Until now, we worked with component instances which didn’t have any internal state. They only displayed the elements contained within them and used props for different behaviour. They didn’t have a state of their own. They are pure.

In functional programming, pure functions are functions which give the same output for the same input.

Pure functions and pure components are very similar. Functions take arguments as input while components take props as input.

Pure_Functions.png

On the contrary impure functions may not return the same value for the same inputs. They can have an internal state to them, or they can be referring to global values for computation.

Impure_Functions.png

Components are powerful with state

State makes components much more powerful. They allow components to have their own internal memory and handle much more powerful interactions. To create a state variable for a component in modern React, we need to start using hooks.

We need to prepare some UI for the hooks demo first

Let’s modify our blogpost page that currently looks like this.


//pages/blogpost.js
import Heading from '../src/components/Heading'

function Post(){
    return <div>
        <Heading>
            Delightful React
        </Heading>
        <img 
          src="https://assets.delightful-react.com/scenery.jpg"
        />
        <p>The best way to learn React!</p>
        <a href="https://google.com">Google</a>        
        <br/>
        <button>Click Me!</button>
      </div>
  }

export default Post;

Let’s add a couple of components and use them inside the Post component.

//pages/blogpost.js
import Heading from "../src/components/Heading";

function PostBody() {
  return (
    <div className="blogpost">
      <Heading>Delightful React</Heading>
      <img 
        src="https://assets.delightful-react.com/scenery.jpg" 
        className="featured-image" 
      />
      <p className="caption">
      Scelerisque consectetur posuere lacinia est parturient elementum ...
      </p>
      <div className="tags-and-upvote">
        <div className="tags">
          <a>React</a>
          <a>Javascript</a>
          <a>Delightful</a>
        </div>
        <button className="upvote-button">
          Click me
        </button>
      </div>
      <p>Ultrices ac bibendum lacinia ...</p>
    </div>
  );
}

function AddComment() {
  return (
    <div className="add-comment">
      <h2>Submit a comment</h2>
      <form>
        <div className="input-row">
          <input type="text" />
          <input type="email" />
        </div>
        <textarea />
        <button>Submit</button>
      </form>
    </div>
  );
}


function Post() {
  return (
    <div>
      <PostBody/>
      <button >Add A Comment</button>
      <AddComment/>
    </div>
  );
}


export default Post

We just added a couple of components and split the code. Our blogpost page is a lot more fleshed out now.

Group_3-3.png

Now, let’s use state within our PostBody.

  1. Let’s start use the “Add Comment” button to show the comment form. Initially the form will be hidden
  2. When the "Add a Comment" is clicked, we want to show open the form.

To achieve this, we want our component to maintain boolean state variable which represents whether the comment form is visible or not. Initially that state variable is false and when the button is clicked, we want to use an event handler to change the state to true. To create a state variable, we need to talk about React hooks.

React hooks

React hooks super charge React components. Function components in React start off as pure components, but can be made stateful using hooks.

HsC.png

React Hooks are special functions. Hooks are functions which are meant to be used only inside React components and different hooks solve different purposes. Let’s use the useState hook that creates a state variable inside a component like so.

useState and it’s usage.

To create a state variable we need to do import the useState hook from React.

import {useState} from 'react'

The useState hook function takes the initial value of the state variable as argument. It returns an array of 2 items. The first item in the array is the state variable value itself. The second item in the array is a function which can be used to update the state variable and make the component update and rerender.

function MyComponent(){
    const stateInfoArray = useState(5)
    const value = stateInfoArray[0] //5
    const setValue = stateInfoArray[1] // function
    ...
}

We used a variable called stateInfo which will be the array that useState returns. We then grabbed value and setState from the array. If we use useState like this, then value, the state variable, is initialised to 5 and it can later be changed in the component using setValue. A shorter syntax to do the same thing would be to use array destructuring.

function MyComponent(){
    const [value,setValue] = useState(5)
    ...
}

Now, let’s use this inside our Post component like so.

function Post() {
  const [showCommentUI, setShowCommentUI] = useState(false)
  return (
    <div>
      <PostBody/>
      <button >Add A Comment</button>
      {showCommentUI ? <AddComment/> : null}
    </div>
  );
}

This will create a state variable named showCommentUI whose value is false.

When used this as a conditional ternary operator expression, the AddComment won’t be visible anymore. It will only show up if showCommentUI’s value is true.

Updating state

The useState hook returns an array of 2 values. The state variable and also a function to update the state variable. This function simply needs to be called with the new value that the state variable needs to be updated to. Calling this function will update the state variable and it triggers a rerender in the component instance. Let’s tie that with the button’s onClick event handler and use it like so.

<button onClick={function(){
        setShowCommentUI(true)
}}>Add A Comment</button>

This is how our Post component should now look like.

function Post() {
  const [showCommentUI, setShowCommentUI] = useState(false)
  return (
    <div>
      <PostBody/>
      <button >Add A Comment</button>
      {showCommentUI ? <AddComment/> : null}
    </div>
  );
}

Try clicking on the button now. We should have our comment form appear after we click on.

State change will automatically cause React to update

Updating state, will cause the React component to immediately rerender. This allows the component to reflect the updated state values into the DOM elements immediately. This ensures that our components are always upto date with the latest changes in the React component.

Component_rerenders_after_state_update.png

Each time we update our state variables, React will rerender the component and flush updates to the DOM immediately. Let’s take our Post component a step further. We can make our button toggle the value from false to true and then back from true to false like so.

<button onClick={function(){
        if(showCommentUI){
          setShowCommentUI(false)
        }else{
          showCommentUI(true)
        }
      }}>
        {showCommentUI ? "Hide Comment Form" : "Add A Comment" }
</button>

An even shorter version of this will look like this. We can simply negate the existing value of the state variable and call setShowCommentUI with the negated value.

function Post() {
  const [showCommentUI, setShowCommentUI] = useState(false)
  return (
    <div>
      <PostBody/>
      <button onClick={function(){
        setShowCommentUI(!showCommentUI)
      }}>{showCommentUI ? "Hide Comment Form" : "Add A Comment" }</button>
      {showCommentUI ? <AddComment/> : null}
    </div>
  );
}

Nice! Isn’t it?

States, components and component instances

Components are function definitions and hooks are declared inside components. But each time we render a component as a new instance, they all have their own separate internal state.

Each_instance_has_its_own_local_state_Copy_2.png

For eg, imagine a React component that renders a button and tracks number of times a button was clicked in a state variable.


function Counter(){
  const [counter, setCounter] = useState(0)
  return <button onClick={function(){
    setCounter(counter+1)
  }}>{counter}</button>
}

If we render two instances of this component and click the first button 5 times and the second button 2 times, their internal state counters are updated to 5 and 2 respectively independent of each other.

<Counter/>
<Counter/>

In other words, we created a Counter component and created 2 component instances of it and each of these 2 instances have their own independent state.

Group_3-4.png

Multiple hooks in the same component

A component have any number of hooks.

Hooks_add_features_to_your_components.png

Let’s move the counter logic into our Post component as an example.


function Post() {
  const [showCommentUI, setShowCommentUI] = useState(false);
  const [counter, setCounter] = useState(0);
  return (
    <div>
      <PostBody />
      <button
        onClick={function () {
          setShowCommentUI(!showCommentUI);
        }}
      >
        {showCommentUI ? "Hide Comment Form" : "Add A Comment"}
      </button>
      {showCommentUI ? <AddComment /> : null}
      <br />
      <button
        onClick={function () {
          setCounter(counter + 1);
        }}
      >
        {counter}
      </button>
      <br />
    </div>
  );
}

Both state variables can be used and updated in the same component instance.

Group_3.png Our components can have any number of hooks within them and they are all managed independently for each instance of the component. This means that all our component instances to have their own little memory inside React and can function independent of one another.

Hooks are just functions?

Hooks are functions. However, they only should be used inside components. There are also some rules that you need to follow when you use hooks. For eg,

  • hooks should not be used inside an if-else condition,
  • hooks should not be used anywhere else except at the top level of the component ie., not inside a callback that will execute on a button click etc.

These rules are peculiar but are very important. To understand these rules and why they are even needed, we need to understand the internals of a React component instance a bit more.

Keep it going!

We have just started talking about hooks. There is still some way to go. Next, let's talk about the internal of hooks and the rules governing hooks. See you in the next chapter!

Thanks and Please support my work

Writing blog posts and making videos is an effort that takes many hours in my day. I do it because I love teaching and make great content. However, I need your support too. Please support my work and follow my social accounts to help me continue to make great content. Here are my social links.

Follow me on Twiter

Subscribe to my channel on Youtube

Thank you!

No Comments Yet