Problem Statement
Build a TypingEffect component that types out a given text one character at a time, deletes it, and repeats forever.
Functional Requirements
- Props: Take a
textstring and adelayin milliseconds between steps. - Type: Start from an empty string and add one character per tick until the full text shows.
- Delete and loop: Once the full text shows, remove one character per tick back to empty, then start typing again.
- Cursor: Show a
|cursor after the text that blinks with a CSS animation.
Technical Constraints
- Use one
setIntervaland clear it in theuseEffectcleanup. - Keep the typing position and direction in a
useRef, so they do not cause extra renders.
Code Breakdown
1. CSS Animation (styles.css)
span {
animation: hideAndShow 1s infinite;
}
@keyframes hideAndShow {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}Explanation:
- Creates a blinking cursor effect for the
<span>element hideAndShowanimation toggles opacity between 0 and 1 every secondinfinitemakes the animation loop continuously
2. App Component (App.js)
import React from "react";
import TypingEffect from "./components/TypingEffect";
const App = () => {
return (
<div>
<TypingEffect text="I'm a FullStack Developer" delay="100" />
</div>
);
};Explanation:
- Main component that renders the
TypingEffectcomponent - Passes two props:
text: The string to be typeddelay: Time (ms) between each character addition/removal
3. TypingEffect Component (TypingEffect.js)
import { useEffect, useRef, useState } from "react";
const TypingEffect = ({ text, delay }) => {
const [displayText, setDisplayText] = useState("");
const velocityRef = useRef({ speed: 1, endIndex: 0 });
useEffect(() => {
const intervalId = setInterval(() => {
if (velocityRef.current.endIndex >= text.length) {
velocityRef.current.speed = -1; // Switch to deleting mode
} else if (velocityRef.current.endIndex === 0) {
velocityRef.current.speed = 1; // Switch to typing mode
}
velocityRef.current.endIndex += velocityRef.current.speed;
setDisplayText(text.slice(0, velocityRef.current.endIndex));
}, delay);
return () => clearInterval(intervalId);
}, [delay, text]);
return (
<div>
{displayText}
<span className="">|</span>
</div>
);
};JavaScript/React Concepts Used
1. React Hooks
useState: Manages the currently displayed textuseEffect: Handles the side effect of typing animationuseRef: Stores mutable values that persist across renders without causing re-renders
2. Key Methods
setInterval: Repeatedly executes the typing/deleting logic at specified intervalsclearInterval: Cleans up the interval when component unmountsArray.slice: Extracts portion of the text to display
3. Why useRef?
- We need to track
speedandendIndexbetween renders - Changing these values shouldn't trigger re-renders
- Regular variables would reset on each render
Code Flow
-
Initialization:
- Component mounts with
displayTextinitialized to an empty string velocityRefinitialized withspeed: 1(typing) andendIndex: 0
- Component mounts with
-
useEffect Execution:
- Sets up an interval that runs every
delaymilliseconds - Each interval tick:
- Checks if we've reached text end (switch to deleting)
- Checks if we're back at start (switch to typing)
- Updates
endIndexbased on currentspeed - Updates
displayTextwith appropriate slice of text
- Sets up an interval that runs every
-
Rendering:
- Shows current
displayTextwith blinking cursor (|)
- Shows current
DRY Run Example
Let's simulate with text="Hi" and delay=100ms:
| Time (ms) | endIndex | speed | displayText | Action |
|---|---|---|---|---|
| 0 | 0 | 1 | "" | Initialize |
| 100 | 1 | 1 | "H" | Add first character |
| 200 | 2 | 1 | "Hi" | Add second character |
| 300 | 2 | -1 | "Hi" | Hit length, reverse |
| 400 | 1 | -1 | "H" | Remove last char |
| 500 | 0 | -1 | "" | Back to start |
| 600 | 0 | 1 | "" | Hit start, reverse |
| 700 | 1 | 1 | "H" | Cycle repeats... |
This creates an infinite typing/deleting loop for the text "Hi".