Have you ever come across the Instagram or YouTube comment section modal where you can swipe up to enlarge the section and swipe down to collapse it? We'll discover how to use React to build a similar comment section modal on this blog.
UseState, useRef, and useEffect are the specific React hooks that we'll be applying. The useRef hook is used to generate a reference to a DOM element, the useEffect hook is used to execute side effects, and the useState hook is used to manage state.
Create an App-style functional component first. We will define the state variables used to control the comment section's height and whether it is being dragged or transitioned inside of this component. Additionally, we will also create a reference to the comment section using the useRef hook.
import { useState, useRef, useEffect} from "react";
export default function App() {
const [height, setHeight] = useState(328);
const [isDragging, setIsDragging] = useState(false);
const [initialY, setInitialY] = useState(0);
const [currentY, setCurrentY] = useState(0);
const sectionRef = useRef(null);
const [isTransitioning, setIsTransitioning] = useState(false);
...
}
The touch event handlers that will be triggered when a user touches, moves, and releases their finger from the comment section will then be defined. The beginning Y coordinate of the touch will be set by the handleTouchStart method, and isDragging will be set to true. The height of the comment section will be adjusted by the handleTouchMove function in accordance with the calculation of the difference between the initial Y coordinate and the current Y coordinate. Set to false by the handleTouchEnd method, isDragging.
const handleTouchStart = (e) => {
setInitialY(e.touches[0].clientY);
setIsDragging(true);
};
const handleTouchEnd = () => {
setIsDragging(false);
};
const handleTouchMove = (e) => {
if (!isDragging) return;
setCurrentY(e.touches[0].clientY);
const diff = initialY - currentY;
setHeight(height + diff);
setInitialY(currentY);
};
Now, we will use the useEffect hook to detect when the user has dragged the comment section to a height of fewer than 200 pixels. If this happens, we will set isTransitioning to true, which will trigger a CSS transition that will collapse the comment section.
useEffect(() => {
if (isDragging && height < 200) {
setIsTransitioning(true);
}
}, [height, isDragging]);
Finally, we will render the comment section as a div with the ref attribute set to the sectionRef reference that we created earlier. We will also add the touch event handlers that we defined earlier and set the style attribute to adjust the height and add a transition effect.
return (
<section className="app">
<div
ref={sectionRef}
onTouchStart={handleTouchStart}
onTouchEnd={handleTouchEnd}
onTouchMove={handleTouchMove}
style={{
height: `${isTransitioning ? 0 : height}px`,
position: "relative",
maxHeight: "600px",
transition: `${isTransitioning && "height 0.6s ease-in-out"}`,
}}
className="card"
>
<div className="grab-container">
<span className="grab"></span>
</div>
</div>
</section>
);
That's all, then! Now, you have a fully functional comment section modal that resembles Instagram/Youtube and can be enlarged and collapsed using touch gestures. Of course, you can alter the comment section's look and behavior to suit your preferences. To create the ideal user experience, you may, for instance, increase the amount of text in the comment section, alter the fonts and colors to match your app's style, or tweak the height restrictions and transition speed.
Additionally, by including more capabilities like comment submission, reply display, or integration with a backend server to store and retrieve comments, you can improve the usefulness of the comment section.
In general, adding a comment area modal similar to Instagram's can be a terrific method to enhance user experience.
Overall, creating a comment section modal like Instagram's can be a great way to improve the user experience of your app and make it more engaging and interactive. With the power of React hooks and the flexibility of CSS, you can achieve this feat in no time!
Complete code -
import "./styles.css";
import { useState, useRef, useEffect} from "react";
export default function App() {
const [height, setHeight] = useState(328);
const [isDragging, setIsDragging] = useState(false);
const [initialY, setInitialY] = useState(0);
const [currentY, setCurrentY] = useState(0);
const sectionRef = useRef(null);
const [isTransitioning, setIsTransitioning] = useState(false);
//touch events
const handleTouchStart = (e) => {
setInitialY(e.touches[0].clientY);
setIsDragging(true);
};
const handleTouchEnd = () => {
setIsDragging(false);
};
const handleTouchMove = (e) => {
if (!isDragging) return;
setCurrentY(e.touches[0].clientY);
const diff = initialY - currentY;
setHeight(height + diff);
setInitialY(currentY);
};
useEffect(() => {
if (isDragging && height < 200) {
setIsTransitioning(true);
}
}, [height, isDragging]);
return (
<section className="app">
<div
ref={sectionRef}
onTouchStart={handleTouchStart}
onTouchEnd={handleTouchEnd}
onTouchMove={handleTouchMove}
style={{
height: `${isTransitioning ? 0 : height}px`,
position: "relative",
maxHeight: "600px",
transition: `${isTransitioning && "height 0.6s ease-in-out"}`,
}}
className="card"
>
<div className="grab-container">
<span className="grab"></span>
</div>
</div>
</section>
);
}
CSS file
.app{
position: fixed;
width: 100%;
height: 100%;
inset: 0;
background: black;
display: flex;
flex-direction: column;
align-items: center;
justify-content: end;
backdrop-filter: blur(12px);
}
.card{
background-color: #333333;
width: 100%;
border-top-right-radius: 12px;
border-top-left-radius: 12px;
}
.grab-container{
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.grab{
width: 40px;
height: 4px;
background: grey;
border-radius: 9999px;
margin-top: 4px;
}