React Hook— Build an Input/Tagging Component

Rajesh Pillai
Unlearning Labs
Published in
5 min readDec 2, 2019

Code a simple tagging component useful for building blog tags, product tags, and many more use cases.

Source Code and demo: React Hooks — Input/Tagging Component

NOTE: I am keeping the code simple for learning/understanding purpose. Also, I am mixing inline/external CSS. Please choose whatever option is best for you.

If you are new to React Hooks then head over to React — Hooks A conceptual Understanding Tutorial

Design

The below diagram shows our component composition. Our <InputTag> component is composed of a TagList component and input component. The TagList component has an array of Tag component.

Step by Step Tutorial

First, let’s create the needed HTML.

<div id="root"></div>

Let’s sprinkle some basic styling

.tag-item {
display: inline-block;
}

Let’s create the App component and see how we intend to use the <InputTag/> component

NOTE: In the codepen I have all the below code in one file, but in case you are running it offline or using create-react-app, the filenames serve as a guide as to where the code should go. (But for demo and learning purpose feel free to put the code in single file as well).

So, in essence, minimal usage is shown below.

<InputTag  
onAddTag ={onAddTag}
onDeleteTag = {onDeleteTag}
defaultTags={tags}
placeholder="enter tags separated by comma"/>

Our <InputTag> takes a placeholder, and onAddTag and onDeleteTag callback to get notified when new tags are added or existing tags are removed and a defaultTags to initialize tags(maybe from database, API’s, etc).

input-tag.js

function InputTag (
{defaultTags, onAddTag, onDeleteTag, placeholder}) {

onKeyUp = (e) => {
console.log(e.which);
// comma (188) for comma, and 13 for enter/return key
if (e.which === 188 || e.which === 13) {
let input = e.target.value.trim().split(",");
// return if empty tags
if (input.length === 0 || input[0] === "") return;
onAddTag(input);
e.target.value = "";
}
}
onDelete = (tag) => {
onDeleteTag(tag);
}
return (
<div style={cStyle}>
<TagList tags = {defaultTags}
onDeleteTag = {onDeleteTag}/>
<input
style={iStyle}
onKeyUp= {(e) => onKeyUp(e) }
type="text" placeholder= {placeholder} />
</div>
)
}

So, our input tag component uses yet another component, <TagList/> which actually renders the tag and an HTML input element to create new tags.

<InputTag/> Code Explanation

Let’s take a brief look at the code.

Create Tags state variables

function InputTag ({defaultTags, onAddTag, onDeleteTag, placeholder}) {
const [tags, setTags] = React.useState([]);

Use useState to initialize tags variables.

The render or return value of the component

return (
<div style={cStyle}>
<TagList tags = {tags}
onDeleteTag = {onDeleteTag}/>
<input
style={iStyle}
onKeyUp= {(e) => onKeyUp(e) }
type="text" placeholder= {placeholder} />
</div>
)

So, here we need a TagList component, an onDeleteTag method, and an onKeyUp method.

We will take a look at the <TagList/> component shortly.

The onKeyUp method

The onkeyup method detects comma “,’” or pressing of <enter/return> key. It then grabs the input and if its empty simply return from the function. Otherwise if invokes the onAddTag method passing in the newly keyed in tag.

onKeyUp = (e) => {
// comma (188) , separator
if (e.which === 188 || e.which === 13) {
let input = e.target.value.trim().split(",");
// empty tags
if (input.length === 0 || input[0] === "") return;
onAddTag(input);
e.target.value = "";
}
}

The onDelete method

The onDelete method here is quite simple as it simply calls the props passed to it.

onDelete = (tag) => {
onDeleteTag(tag);
}

Now, let’s take a look at the <TagList> component.

<TagList> Component (tag-list.js)

The TagList Component renders a list of tags. Here is the full code.

const cStyle = {
position: "relative",
display: "inline-block",
width: "300px",
border: "1px solid lightblue",
overflow: "auto"
}
const iStyle = {
display: "inline-block",
fontSize: "0.9em",
margin: "5px",
width: "90%",
border: "0"
}
function TagList({tags, onDeleteTag}) {
let tagsUI = tags.map((tag) => {
return <Tag
onDeleteTag = {()=>onDeleteTag(tag)}
key={tag} value={tag} />
});
return (
<div className="tag-list">
{tagsUI}
</div>
);
}

The TagList component loops through the tags array and creates and <Tag> component for every item in the array. The <Tag> component is passed the onDeleteTag handler as well.

<Tag> Component (tag.js)

Finally here comes the Tag component, which represents one tag item. The tag item renders the tag value and also add a delete icon as well.

The Tag component source code.

const tagStyle  = {
display: "inline-block",
backgroundColor: "yellow",
fontSize: "0.9em",
margin: "5px",
border: "1px solid lightblue",
padding: "2px",
cursor: "pointer"
}
function Tag({onDeleteTag, value}) {
var tag = (
<div class="tag-item">
<span
onClick = {(e) => onDeleteTag(e, value)}
style={tagStyle}>
&#x2716; {" "}
</span>
{value}
</div>
);
return (
<React.Fragment>
{tag}
</React.Fragment>
)
}

The Tag components simply render the individual tag and also has a handler for deleting individual tags.

Let’s see the example application in action.

UseCase: app.js code

The below code demonstrates how to use the <InputTag> component.

function App() {// NOTE:  If you are fetching from remote server/api, you can
// also use the useEffect hook to initialize tags
const [tags, setTags] =React.useState(["javascript", "web dev"]);

onAddTag = (tag) => {
setTags([...tags, tag]);
}

onDeleteTag = (tag) => {
alert(`deleting ${tag}`);
let remainingTags = tags.filter ((t) => {
return (t !== tag);
});
setTags([...remainingTags]);
}

return (
<div>
<InputTag
onAddTag ={onAddTag}
onDeleteTag = {onDeleteTag}
defaultTags={tags}
placeholder="enter tags separated by comma"/>

<div>You can also press
&lt;return&gt;or&lt;enter&gt;key</div>
</div>
)
}

Render the component onto a browser

const rootElement = document.querySelector("#root");
ReactDOM.render(<App />, rootElement);

Code Explanation

First, let’s initialize the tags

const [tags, setTags] =React.useState(["javascript", "web dev"]);

NOTE: In case you are fetching the tags from databases/remote api, use the useEffect hook.

The function returns a <InputTag> and some other details.

return (
<div>
<InputTag
onAddTag ={onAddTag}
onDeleteTag = {onDeleteTag}
defaultTags={tags}
placeholder="enter tags separated by comma"/>

<div>You can also press
&lt;return&gt;or&lt;enter&gt;key</div>
</div>
)

NOTE: As all application state will be owned by the parent component, in this case app.js, we have to handle the actual tag creation, deletion here.

onAddTag method

onAddTag is called when a new tag is added withing the <InputTag> Component. It simply uses the state updater function setTags to update the tags array.

onAddTag = (tag) => {
setTags([...tags, tag]);
}

onDeleteTag method

onDeleteTag removes the tag from the state array and calls the state updater function setTags to set new values.

onDeleteTag = (tag) => {
alert(`deleting ${tag}`);
let remainingTags = tags.filter ((t) => {
return (t !== tag);
});
setTags([...remainingTags]);
}

And this completes your own little tagging component using React Hooks.

History

  • 5-Dec-2019 — Refactored based on Qingyangfeng suggestion (removed useEffect hook from InputTag.
  • 2-Dec-2019 — First published

--

--

Rajesh Pillai
Unlearning Labs

Co-Founder, Software Engineer and CTO @Algorisys Technologies - Building Products and Upskilling upcoming generations