React useMeasure not working with nextJS? - reactjs

I'm currently trying to animate a div so that it slides from bottom to top inside a card.
The useMeasure hook is supposed to give me the height of the wrapper through the handler I attached to it : <div className="desc-wrapper" {...bind}>
Then I am supposed to set the top offset of an absolutely positionned div to the height of its parent and update this value to animate it.
The problem is that when logging the bounds returned by the useMeasure() hook, all the values are at zero...
Here is a link to production exemple of the panel not being slided down because detected height of parent is 0 : https://next-portfolio-41pk0s1nc.vercel.app/page-projects
The card component is called Project, here is the code :
import React, { useEffect, useState } from 'react'
import './project.scss'
import useMeasure from 'react-use-measure';
import { useSpring, animated } from "react-spring";
const Project = (projectData, key) => {
const { project } = projectData
const [open, toggle] = useState(false)
const [bind, bounds] = useMeasure()
const props = useSpring({ top: open ? 0 : bounds.height })
useEffect(() => {
console.log(bounds)
})
return (
<div className="project-container">
<div className="img-wrapper" style={{ background: `url('${project.illustrationPath}') no-
repeat center`, backgroundSize: project.portrait ? 'contain' : 'cover' }}>
</div>
<div className="desc-wrapper" {...bind} >
<h2 className="titre">{project.projectName}</h2>
<span className="description">{project.description}</span>
<animated.div className="tags-wrapper" style={{ top: props.top }}>
</animated.div>
</div>
</div>
);
};
export default Project;
Is this a design issue from nextJS or am I doing something wrong ? Thanks

I never used react-use-measure, but in the documentations, the first item in the array is a ref and you are suppose to use it this way.
function App() {
const [ref, bounds] = useMeasure()
// consider that knowing bounds is only possible *after* the view renders
// so you'll get zero values on the first run and be informed later
return <div ref={ref} />
}
You did...
<div className="desc-wrapper" {...bind} >
Which I don't think is correct...

Related

React how to add class to parent element when child element is clicked

This is my code.
export default MainContent = () => {
handleClick = (e) => {
// This is where I got confused
};
return (
<>
<div>
<div onClick={handleClick}>1</div>
</div>
<div>
<div onClick={handleClick}>2</div>
</div>
<div>
<div onClick={handleClick}>3</div>
</div>
</>
);
}
What I want is to add a class to parent div when child element is clicked. I couldn't use useState() since I only need one element to update. Couldn't use setAttribute since it changes the same element. Is there any solution for that?
I take it you want to apply the class only to direct parent of clicked child.
create a state to oversee different clicked child div
apply the class only to direct parent of clicked* child div based on the state
make use of clsx npm package (since we don't wanna overwrite parent div styling)
you may see the working examples here: https://codesandbox.io/s/happy-babbage-3eczt
import { useState } from "react";
import "./styles.css";
import styled from "styled-components";
import classnames from "clsx";
export default function App() {
const [styling, setstyling] = useState({
status: false,
from: "",
style: ""
});
function handleClick(childNo) {
setstyling({ status: true, from: childNo, style: "applyBgColor" });
}
return (
<div className="App">
<Styling>
<div
className={
styling?.status && styling?.from == "child-1"
? classnames("indentText", styling?.style)
: "indentText"
}
>
<div
className="whenHoverPointer"
onClick={() => handleClick(`child-1`)}
>1</div>
</div>
<div
className={
styling?.status && styling?.from == "child-2"
? styling?.style
: ""
}
>
<div
className="whenHoverPointer"
onClick={() => handleClick(`child-2`)}
>2</div>
</div>
</Styling>
</div>
);
}
const Styling = styled.div`
.indentText {
font-style: italic;
}
.applyBgColor {
background-color: grey;
}
.whenHoverPointer {
cursor: pointer;
}
`;
function Item({ children }) {
const [checked, isChecked] = useState(false);
const onClick = () => isChecked(true);
return (
<div {...(isChecked && { className: 'foo' })}>
<button type="button" onClick={onClick}>{children}</button>
</div>
);
}
function MainContent() {
return [1, 2, 3].map(n => <Item key={n}>{n}</Item>);
}
I think theirs something wrong, useState and JSX will update related part, react will handling that itself, but base on logic, may you need to prevent re-render to stop issue, for example, here on handleClick, will keep re-render in each state since function will re-addressed in memory each update..
any way, base on your question, you can do that by this:
const handleClick = useCallback((e) => {
e.target.parentElement.classList.add('yourClass')
}, []);
But I believe its a Bad solution.
What I recommend is solve issue by state to keep your react life cycle is fully work and listen to any update, also you can use ref to your wrapper div and add class by ref.

How can I correctly update a component's MaxHeight based upon dynamic content?

I've created a React (functional) Accordion component that has its contents fed in through the parent. The code is as follows:
import React, {useEffect, useRef, useState} from 'react';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faChevronUp, faTrashAlt } from '#fortawesome/free-solid-svg-icons';
import './Accordion.css';
const Accordion = ({title, content, active}) => {
const [isActive, setIsActive] = useState(active);
const [expanderStyle, setExpanderstyle] = useState({maxHeight: 0});
const contentDiv = useRef();
useEffect(() => {
setExpanderstyle(isActive ? {maxHeight: contentDiv.current.scrollHeight + 1} : {maxHeight: 0});
}, [isActive]);
const toggle = () => {
setIsActive(!isActive);
}
return (
<div className="accordion">
<div className="accordion-title">
<div>
<button className="btn btn-icon round small" onClick={toggle}><FontAwesomeIcon icon={faChevronUp} className={`icon rotator ${isActive ? "active" : ""}`}/></button>
</div>
<div className="accordion-title--title">
{title}
</div>
</div>
<div ref={contentDiv} className="accordion-content" style={expanderStyle}>
{content}
</div>
</div>
);
}
export default Accordion;
And I have set the styling for my .accordion-content as follows
.accordion-content {
...
overflow: hidden;
transition: max-height 0.4s linear;
}
Which creates a sliding animation for when the user expands / collapses the accordion.
The issue I'm facing is that, since the content is fed in through the parent, if I add in anything that changes the height of the accordion, the height doesn't get updated automatically.
Obviously, I could change the max-height to a large number to show everything, but, then, the animation effect changes dramatically, so I do need to calculate the content's height correctly.
I tried updating my useEffect to include content or contentDiv as part of its dependencies, but it seems the useEffect fires too soon and still doesn't size correctly.
How can I fix this?

How can I toggle a class and change the CSS in Nextjs?

I'm working on a Next.js project where the menu opens with a <p> toggling the menu class. I managed to do this, but when I add the class in the CSS it doesn't take into account both classes.
This is my code:
Component
import { useState } from "react";
import styles from "../styles/modules/header.module.scss";
export default function Component() {
const [isModal, setIsModal] = useState(false);
return (
<div>
<p onClick={() => setIsModal(!isModal)}>Menu</p>
<div className={`${isModal && "nav-open"} ${styles.ModalContainer}`}>
Content
</div>
</div>
);
}
SCSS
.ModalContainer {
position: absolute;
left: -100vw;
&.nav-open {
left: 0;
}
}
When I inspect the code I can see that it adds the class when I click on the menu button, but can't see the expected changes. Does someone have a solution?
You need to use the class from your scoped Sass module file, in this case styles["nav-open"]. Simply setting "nav-open" will refer to a global class, which probably doesn't exist.
export default function Component() {
const [isModal, setIsModal] = useState(false);
const contentClassname = isModal
? `${styles["nav-open"]} ${styles.ModalContainer}`
: styles.ModalContainer;
return (
<div>
<p onClick={() => setIsModal(!isModal)}>Menu</p>
<div className={contentClassname}>Content</div>
</div>
);
}

react How to hide the Card when another location is pressed

Developed with react and typescript.
Now the card is shown or hidden when you click on the div tag.
I want to hide the Card when it is displayed, even if another place other than the div tag is pressed.
import React, { FunctionComponent, useState } from 'react';
import { Card } from 'components/atoms/Card';
import { Display } from 'components/atoms/Display';
const Test: FunctionComponent = () => {
const [isDisplay, setIsDisplay] = useState(false);
const onClick = () => {
setIsDisplay(!isDisplay);
};
return (
<>
<div onClick={onClick} style={{ width: '100px', height: '100px' }}>
display Card
</div>
<Display enabled={isDisplay}>
<Card width={100} height={100}></Card>
</Display>
</>
);
};
export default Test;
Try this in your onClick method. It looks like you need to access the current state's value and update it.
setIsDisplay(state => !state);
It's explained here in the React docs.
https://reactjs.org/docs/hooks-reference.html#functional-updates

Gatsby/React - fade out section on scroll?

I've been trying to use gatsby-plugin-scroll-reveal which uses Sal.js to animate a hero section on my site. I'm trying to make it so that the text in the hero fades in then fades out as you scroll down the page. Right now, I can only get it to fade in. How can I make that happen with Sal.js or another way?
I also tried a different way by creating a component that uses IntersectionObserver DOM API but I couldn't get that to work really.
Here's the component:
import React from 'react';
import ReactDOM from 'react-dom';
function FadeInSection(props) {
const [isVisible, setVisible] = React.useState(true);
const domRef = React.useRef();
React.useEffect(() => {
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => setVisible(entry.isIntersecting));
});
observer.observe(domRef.current);
return () => observer.unobserve(domRef.current);
}, []);
return (
<div
className={`fade-in-section ${isVisible ? 'is-visible' : ''}`}
ref={domRef}
>
{props.children}
</div>
);
}
export default FadeInSection
I figured out a solution from this article:
https://markoskon.com/scroll-reveal-animations-with-react-spring/
So, I'm using the react-spring to create reveal animations on scroll and react-visibility-sensor to see if the I want animated element is visible.
// App.js
import React from "react";
import { Spring } from "react-spring/renderprops";
import VisibilitySensor from "react-visibility-sensor";
<VisibilitySensor once>
{({ isVisible }) => (
<Spring delay={100} to={{ opacity: isVisible ? 1 : 0 }}>
{({ opacity }) => <h1 style={{opacity}}>Title</h1>}
</Spring>
)}
</VisibilitySensor>

Resources