React Hooks: Introduction
Intro
In this post we will use the useState hook to track state in a functional component, and use that state to toggle an accordion element.
Hooks are a way to write reusable code, instead of more classic techniques like Inheritance.
Here are a few examples of Hooks.
useState- Function that lets you use state in a functional component.
useEffect- Function that lets you use something like lifecycle methods in a functional component.
useRef- Function that lets you create a ref in a functional component
The hooks built into React can be combined into custom Hooks, which are small chunks of re-usable code.
Scaffolding The Accordion Widget
We are going to make an accordion widget for our first example. Here is the basic scaffold.
The App component has the accordion contents in a simple array that gets passed into the Accordion widget as a prop.
import React from "react"
import Accordion from "./components/Accordion"
const items = [
{
title: "Excepteur sint occaecat",
content:
"Morbi tristique senectus et netus et malesuada. Eleifend quam adipiscing vitae proin sagittis nisl rhoncus mattis rhoncus. Morbi tincidunt ornare massa eget egestas purus viverra. Ut tristique et egestas quis ipsum. Nunc faucibus a pellentesque sit amet porttitor eget. Praesent elementum facilisis leo vel fringilla est ullamcorper eget nulla. ",
},
{
title: "Duis aute irure dolor",
content:
"Augue interdum velit euismod in pellentesque massa placerat. Id aliquet lectus proin nibh nisl condimentum id venenatis. Non enim praesent elementum facilisis leo vel fringilla. Semper eget duis at tellus at urna condimentum mattis pellentesque. ",
},
{
title: "Ut enim ad minim",
content:
"Lacus luctus accumsan tortor posuere ac ut consequat semper. Suscipit adipiscing bibendum est ultricies integer quis auctor. Nunc sed velit dignissim sodales ut. Vestibulum morbi blandit cursus risus at ultrices mi. Netus et malesuada fames ac turpis egestas integer.",
},
]
export default () => {
return (
<div>
<Accordion items={items} />
</div>
)
}
The Accordion widget then maps out the items in the array into some simple div
s styled with Semantic UI classes. Note that we have added a click listener on them to console log the index of each item when we click on the title. This is what we will use to toggle the active class.
import React from "react"
const Accordion = ({ items }) => {
const renderedItems = items.map((item, index) => {
return (
<React.Fragment key={item.title}>
<div
className="title active"
onClick={() => console.log("Title clicked", index)} // highlight-line
>
<i className="dropdown icon"></i>
{item.title}
</div>
<div className="content active">
<p>{item.content}</p>
</div>
</React.Fragment>
)
})
return <div className="ui styled accordion">{renderedItems}</div>
}
export default Accordion
What it looks like currently.
The console logs are working.
Breaking the Click Listener into Helper Function
At this point it would be a good practice to break this click listener function out into a helper function instead of putting all of the logic for that inline. Right now it is relatively simple, but it could become more complex, or it could be re-used several times. Let's break this out into a helper function like so.
import React from "react"
const Accordion = ({ items }) => {
const onTitleClick = index => {
console.log("Title clicked", index)
}
const renderedItems = items.map((item, index) => {
return (
<React.Fragment key={item.title}>
<div className="title active" onClick={() => onTitleClick(index)}>
{" "}
//highlight-line
<i className="dropdown icon"></i>
{item.title}
</div>
<div className="content active">
<p>{item.content}</p>
</div>
</React.Fragment>
)
})
return <div className="ui styled accordion">{renderedItems}</div>
}
export default Accordion
Note that we have passed the index
into the onTitleClick()
function as an argument to make sure that info passes through to the helper function.
Adding the useState Hook
Now we are going to add our first hook to this component. We start by importing the useState
hook from React. Then we define the hook, and down at the bottom we display the current number of the active index, which defaults to null.
import React, { useState } from "react" // highlight-line
const Accordion = ({ items }) => {
const [activeIndex, setActiveIndex] = useState(null)
const onTitleClick = index => {
setActiveIndex(index)
}
const renderedItems = items.map((item, index) => {
return (
<React.Fragment key={item.title}>
<div className="title active" onClick={() => onTitleClick(index)}>
<i className="dropdown icon"></i>
{item.title}
</div>
<div className="content active">
<p>{item.content}</p>
</div>
</React.Fragment>
)
})
return (
<div className="ui styled accordion">
{renderedItems}
<h1>{activeIndex}</h1> // highlight-line
</div>
)
}
export default Accordion
And at this point as we click the different titles the number on the screen changes. However we completely glossed over what is happening with the actual hook itself.
Explaining the Hook
Let's go over in detail what we did here
import React, { useState } from "react"
const Accordion = ({ items }) => {
const [activeIndex, setActiveIndex] = useState(null)
const onTitleClick = index => {
setActiveIndex(index)
}
const renderedItems = items.map((item, index) => {
return (
<React.Fragment key={item.title}>
<div className="title active" onClick={() => onTitleClick(index)}>
<i className="dropdown icon"></i>
{item.title}
</div>
<div className="content active">
<p>{item.content}</p>
</div>
</React.Fragment>
)
})
return (
<div className="ui styled accordion">
{renderedItems}
<h1>{activeIndex}</h1>
</div>
)
}
export default Accordion
useState
is one of the primitive hooks that React gives us access to. It's entire purpose is to allow you to use state in a functional component, which you may want to do when the state of the component is completely irrelevant to the rest of the application.
The next part is a bit tricky:
const [activeIndex, setActiveIndex] = useState(null)
This looks like we are creating an array, but we aren't. This is actually something called destructuring arrays.
What is happening is that the array already exists, it is defined by useState()
, and we are assigning variables to the first and second items in that array, and calling them activeIndex
and setActiveIndex
respectively. This is just a shortcut instead of writing:
const activeIndex = useState[0]
const setActiveIndex = useState[1]
Calling useState
always returns an array with two items. We are simply anticipating that and assigning constants to both of those items in the beginning. The names of these constants are not set, we can change these to whatever we want. However the first variable is always the current value of the state, and the second value is the name of the function that we want to call to update the state. If we were keeping track of name is could be [name, setName], colors could be [color, setColor] etc etc.
Knowing this it now makes much more sense inside of our helper function.
const onTitleClick = index => {
setActiveIndex(index)
}
When we click on the title we are using the setActiveIndex
function and passing in index
.
Expanding Accordion
In order to make our variables a little bit more semantic I have changed the term index to tab. Now that we have the ability to change the value of activeTab
we need to add a checker function inside of our .map
loop. If the index of the current item is the same as the activeTab
then we want to insert the class "active" into the div.
import React, { useState } from "react";
const Accordion = ({ items }) => {
const [activeTab, setActiveTab] = useState(null);
const onTitleClick = (index) => {
setActiveTab(index);
};
const renderedItems = items.map((item, index) => {
// if current index is same as active tab, add class "active"
const activeStatus = index === activeTab ? 'active' : ''; // highlight-line
return (
<React.Fragment key={item.title}>
<div className={`title ${activeStatus}`} onClick={() => onTitleClick(index)}> // highlight-line
<i className="dropdown icon"></i>
{item.title}
</div>
<div className={`content ${activeStatus}`}> // highlight-line
<p>{item.content}</p>
</div>
</React.Fragment>
);
});
return (
<div className="ui styled accordion">
{renderedItems}
</div>
);
};
export default Accordion;
There aren't any smooth transition animations, and they don't toggle like you would expect, but if you click them they open and the others close, which is what we wanted. Huzzah.
GitHub Repo
Comments
Recent Work
Basalt
basalt.softwareFree desktop AI Chat client, designed for developers and businesses. Unlocks advanced model settings only available in the API. Includes quality of life features like custom syntax highlighting.
BidBear
bidbear.ioBidbear is a report automation tool. It downloads Amazon Seller and Advertising reports, daily, to a private database. It then merges and formats the data into beautiful, on demand, exportable performance reports.