Passing className values using useLocation - reactjs

Im creating a project that needs to have variable styles and images dependent on the route its in so that i dont have to recreate components.
I've successfully gotten images and text in in the code below. but i am unsuccessful in getting the string value for className in the first div into the css.
Please help!
import React, { Fragment, useEffect } from "react";
import { useLocation } from "react-router-dom";
import homeimg from "../../images/homeimg.jpg";
import consimg from "../../images/consimg.jpg";
import solsimg from "../../images/solsimg.jpg";
const Hero = () => {
useEffect(() => {}, []);
const location = useLocation();
const { pathname } = location;
let img = null;
if (pathname === "/") {
img = homeimg;
} else if (pathname === "/consultants") {
img = consimg;
} else if (pathname === "/solutions") {
img = solsimg;
}
return (
<Fragment>
<div
className={
{pathname === "/" && ("grid-home")}
{pathname === "/consultants" && ("grid-consultants")}
{pathname === "/solutions" && ("grid-solutions")}
}>
<div className='overlay'>
<div>
<p className='bg-dark'></p>
<img src={img} alt='' />
</div>
</div>
<div className='copy'>
{pathname === "/" && (
<div>
<h1 className='text-primary'>Snorem Snipsem</h1>
<h3 className='text-danger'>Lorem ipsum dolor sit.</h3>
<p className='text-light'>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
<br />
Repellat nemo, in odit culpa, illo earum voluptatum
<br />
aliquam quaerat iure sunt, quos similique quod <br />
Recusandae voluptates voluptatum nisi sint dicta.
</p>
</div>
)}
{pathname === "/consultants" && (
<div>
<h1 className='text-primary'>Lorem, ipsum.</h1>
<h3 className='text-danger'>Lorem ipsum dolor sit.</h3>
<p className='text-light'>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
<br />
Repellat nemo, in odit culpa, illo earum voluptatum
<br />
aliquam quaerat iure sunt, quos similique quod <br />
Recusandae voluptates voluptatum nisi sint dicta.
</p>
</div>
)}
{pathname === "/solutions" && (
<div>
<h1 className='text-primary'>Forem, ipsum.</h1>
<h3 className='text-danger'>Lorem ipsum dolor sit.</h3>
<p className='text-light'>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
<br />
Repellat nemo, in odit culpa, illo earum voluptatum
<br />
aliquam quaerat iure sunt, quos similique quod <br />
Recusandae voluptates voluptatum nisi sint dicta.
</p>
</div>
)}
</div>
</Fragment>
);
};
export default Hero;

At first declare it and try to use
let cn = "";
if(pathname === '/'){
cn = grid-home
}
....
return(
<div className={cn}>
..
..

You may use classnames library in order to add dynamically classnames https://www.npmjs.com/package/classnames
const divClass = classNames({
'grid-home': pathname === "/" ,
'grid-consultants': pathname === "/consultants",
'grid-solutions': pathname === "/solutions"
});
<div className={divClass}>

Related

How to make animations works with IntersectionObserver in React app with Tailwind

I'm stuck. So what do I want from my app:
to animate sections with left fading and opacity (0 to 1) when I scroll down or up.
reuse this component later with different separate components.
What I have now:
Js function that perfectly works with simple HTML and CSS.
nothing from my 'want list'
Please help!
My code is next:
import React from 'react';
const TestAnimation = () => {
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add('.opacity-100');
} else {
entry.target.classList.remove('.opacity-100');
}
});
});
const hiddenElements = document.querySelectorAll('.opacity-0');
hiddenElements.forEach((el) => {
observer.observe(el);
});
return (
<div className='m-0 bg-slate-900 p-0 text-white'>
<section className='grid min-h-screen place-items-center content-center opacity-0'>
<h1>Test</h1>
</section>
<section className='grid min-h-screen place-items-center content-center opacity-0'>
<h2>This is first page</h2>
<p>
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Autem modi voluptatem est iste a commodi
nesciunt saepe quisquam id dignissimos odit, repellat asperiores laboriosam quibusdam expedita
itaque blanditiis eos pariatur.
</p>
</section>
<section className='grid min-h-screen place-items-center content-center opacity-0'>
<h2>This is third page</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consectetur veniam sint illo quas beatae,
eum omnis, deleniti error, eveniet praesentium fugiat quia quod? Maxime, placeat reiciendis ab
debitis exercitationem nemo. Laborum perspiciatis eum architecto laboriosam, necessitatibus
voluptatibus cupiditate accusantium corrupti placeat mollitia omnis tenetur! Incidunt fugiat
possimus quod, quidem itaque ducimus, perspiciatis eligendi, commodi voluptate cupiditate nihil
corrupti soluta maxime.
</p>
</section>
<section className='grid min-h-screen place-items-center content-center opacity-0'>
<h2>this is Fourth page</h2>
<p className='text-center'>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quos similique harum, officiis facere odit
adipisci maxime obcaecati placeat, quibusdam totam magni eaque? Dicta id commodi saepe dignissimos
quam unde eaque.
</p>
</section>
</div>
);
};
export default TestAnimation;
You should move the initiation of the intersection observer into a useEffect hook.
Here is an example of an Observer component I've used in the past:
export default function Observer({ children, sectionRef, callback }) {
function onObserver(entries) {
const entry = entries[0];
console.log(entry);
if (entry.isIntersecting) {
callback(sectionRef.current.id);
console.log("element in:", entry.target.id);
} else {
console.log("element left:", entry.target.id);
}
}
useEffect(() => {
const refCopy = sectionRef;
let options = {
root: null,
rootMargin: "0% 0% -5% 0%",
threshold: [0.5],
};
let observer = new IntersectionObserver(onObserver, options);
if (refCopy.current) {
observer.observe(refCopy.current);
}
return () => {
if (refCopy.current) {
observer.unobserve(refCopy.current);
}
};
}, [sectionRef]);
return <div id="observation">{children}</div>;
}
And here is how to use it:
export default function Education({ children, handleInView }) {
const sectionRef = useRef(null);
return (
<Observer sectionRef={sectionRef} callback={handleInView}>
<section
ref={sectionRef}
id="education"
className="education"
data-scroll-section
data-scroll
data-scroll-class="purpleColor"
>
<Pencils />
<div className="details">
<div className="side-by-side">
<div className="title" data-scroll data-scroll-speed={2}>
<span>Fullstack</span>
<span> Software </span>
<span>Engineer</span>
</div>
</div>
</div>
</section>
</Observer>
);
}

ReactComponent renders the same SVG Element

I am importing an SVG element using the ReactComponent method as below:
import { ReactComponent as Height } from "../../assets/Height.svg";
import { ReactComponent as Closet } from "../../assets/Closet.svg";
import { ReactComponent as Shirt } from "../../assets/Shirt.svg";
When I render them on the screen like this:
<Height />
<Shirt />
<Closet />
It shows the first Icon for all the three renders, for example, for the above code it renders the Height SVG element for all of them.
Here is the full component:
import React from "react";
import styles from "./Features.module.css";
import featureImage from "../../assets/featureImage.png";
import { ReactComponent as Height } from "../../assets/Height.svg";
import { ReactComponent as Closet } from "../../assets/Closet.svg";
import { ReactComponent as Shirt } from "../../assets/Shirt.svg";
import FeatureBackground from "../../assets/FeatureBackground.png";
const Features = () => {
const bodyContent = [
{
icon: <Height />,
title: "Body measurement tracking",
body: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi ",
},
{
icon: <Closet />,
title: "In home trial of clothes and closet",
body: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi ",
},
{
icon: <Shirt />,
title: "Recommendation of clothes using AI",
body: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi ",
},
];
return (
<div className={styles.container}>
<div className={styles.mainSection}>
<div className={styles.images}>
<img
className={styles.featureBackground}
src={FeatureBackground}
alt="feature background"
/>
<img
className={styles.featureImage}
src={featureImage}
alt="features Image"
/>
</div>
<div className={styles.content}>
<h1>EVERYTHING YOU NEED!</h1>
<div className={styles.body}>
{bodyContent.map((content, i) => (
<div key={i} className={styles.bodyContent}>
<div className={styles.icon}> {content.icon} </div>
<div>
<h3>{content.title}</h3>
<p>{content.body}</p>
</div>
</div>
))}
</div>
</div>
</div>
<div className={styles.footerSection}>
<h2>
Enhance your shopping experience with elevated expertise and efficient
time constraints.
</h2>
</div>
</div>
);
};
export default Features;

how to dynamically fetch data in react while deploying?

Here's the thing, it all works fine if I run it with the npm run dev script, but images in testimonials.avatar don't render with npm run build.
const Testimonials = ({ testimonials }) => {
return (
<div id='testimonials' className='mt-20'>
<div className='text-center mb-8'>
<p className='text-xs uppercase mb-4 md:text-base'>Testimonials</p>
<h1 className='text-3xl md:text-5xl font-bold capitalize mb-10'>Read What Other<br />have to Say</h1>
</div>
<div className='flex flex-col gap-4 items-center md:flex-row'>
{testimonials.map(item =>
<div className='bg-light-gray rounded-3xl p-8 transition-transform duration-300 hover:-translate-y-2'>
<div className='w-32 h-32 mx-auto mb-4'>
<img src={item.avatar} alt="Person's avatar" className='rounded-full border' />
</div>
<p className='font-bold text-center'>{item.fullName}</p>
<p className='text-center mt-6'>{item.feedback}</p>
</div>
)}
</div>
</div>
)
}
Here I have a component which receives an array testimonials through props and then renders them in a div.
This is the array in App.jsx file.
const testimonials = [
{
avatar: '../src/images/avatars/avatar-1.png',
fullName: 'Andrew Rathore',
feedback: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc ullamcorper scelerisque mi, in malesuada felis malesuada vel. '
},
{
avatar: '../src/images/avatars/avatar-2.png',
fullName: 'Vera Duncan',
feedback: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc ullamcorper scelerisque mi, in malesuada felis malesuada vel. '
},
{
avatar: '../src/images/avatars/avatar-3.png',
fullName: 'Mark Smith',
feedback: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc ullamcorper scelerisque mi, in malesuada felis malesuada vel. '
}
];
I guess I know that the main reason why it does not work while deploying is because of a non-existent path since I will no longer have src/images/avatar during a deploy.
I also guess this is a dumb question because I am quite new to React and JavaScript in general, but would appreciate any answer regarding the problem.
You have to wrap the avatar path in require() like require('../src/images/avatars/avatar-1.png') I hope this works. thanks
Fixed this by creating an external file and then fetching it to the component like so.
data.js
import avatar1 from '../public/images/avatars/avatar-1.png';
import avatar2 from '../public/images/avatars/avatar-2.png';
import avatar3 from '../public/images/avatars/avatar-3.png';
export const testimonials = [
{
authorImg: avatar1,
authorName: 'Andrew Rathore',
authorText: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc ullamcorper scelerisque mi, in malesuada felis malesuada vel.'
},
{
authorImg: avatar2,
authorName: 'Vera Duncan',
authorText: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc ullamcorper scelerisque mi, in malesuada felis malesuada vel.'
},
{
authorImg: avatar3,
authorName: 'Mark Smith',
authorText: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc ullamcorper scelerisque mi, in malesuada felis malesuada vel.'
}
];
Testimonials.jsx
import React from 'react';
import { testimonials } from '../data/data';
const Testimonials = () => {
return (
<div className='flex flex-col gap-4 justify-between items-center md:flex-row'>
{testimonials.map((testimonial, index) =>
<div key={index} className='bg-light-gray rounded-3xl p-8 transition-transform duration-300 hover:-translate-y-2'>
<div className='w-32 h-32 mx-auto mb-4'>
<img src={testimonial.authorImg} alt="Person's avatar" className='rounded-full border' />
</div>
<p className='font-bold text-center'>{testimonial.authorName}</p>
<p className='text-center mt-6'>{testimonial.authorText}</p>
</div>
)}
</div>
)
}
export default Testimonials;
The main reason why it didn't work well in the first place was the fact that I specified path to the images in the object itself like so:
{
key: '../../../value.png'
}
That is why I ran into as error every time while deploying, 'cause it simply couldn't find that path due to its non-existence.

Problem fetching data in react using getStaticProps

I'm currently trying to fetch some data in my Next.js project using the getStaticProps() function and I continue to get this error:
TypeError: Cannot read property 'map' of undefined
My code looks like this:
export const getStaticProps = async () => {
const data = [
{
"id": 1,
"question": "Lorem ipsum dolor sit amet consectetur adipisicing elit.",
"answer": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Iste magni at magnam placeat. Non, saepe?"
},
{
"id": 2,
"question": "Lorem ipsum dolor sit amet consectetur adipisicing elit.",
"answer": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Iste magni at magnam placeat. Non, saepe?"
}
]
return {
props: {
questions: data
}
}
}
export default function FAQ({ questions }) {
return (
<div className="p-8 grid bg-blanco" >
<div className="grid place-content-center text-center mt-10 md:mt-0" >
<h1 className="text-2xl text-gun-rose-700 font-bold" >¿Tiene preguntas? Mira aquí</h1>
<h3 className="text-gun-rose-300 mt-4 max-w-2xl" >Lorem ipsum dolor sit amet consectetur adipisicing elit. Sunt, suscipit. Aliquid molestias eveniet ullam? Dolores, minus? Perspiciatis neque voluptates iste!</h3>
</div>
{questions.map(q => (
<div className="" key={q.id}>
<h3 className="">{q.question}</h3>
<p className="">{q.answer}</p>
</div>
))}
</div>
)
}
Any guidence will be appreciated, thanks in advance :)
It could be an async issue where questions is not immediately available. Try with a null check at the start of questions.
export default function FAQ({ questions }) {
return (
<div className="p-8 grid bg-blanco" >
<div className="grid place-content-center text-center mt-10 md:mt-0" >
<h1 className="text-2xl text-gun-rose-700 font-bold" >¿Tiene preguntas? Mira aquí</h1>
<h3 className="text-gun-rose-300 mt-4 max-w-2xl" >Lorem ipsum dolor sit amet consectetur adipisicing elit. Sunt, suscipit. Aliquid molestias eveniet ullam? Dolores, minus? Perspiciatis neque voluptates iste!</h3>
</div>
{questions && questions.map(q => (
<div className="" key={q.id}>
<h3 className="">{q.question}</h3>
<p className="">{q.answer}</p>
</div>
))}
</div>
)
}
Edit 1:
The getStaticProps method can only be used on the top level component of a page as stated in the caveats
The fix is to move the getStaticProps method to the top level page component. I've demo'ed this in a sandbox as folder structures are so important, link here.

"Your render method should have a return statement" when I do have a return statement

So basically what I am trying to do here is set the toggle state for my modal and then toggle the module on and off via the alert and that should work fine hopefully. However for some reason I am getting the error "Your render method should have a return statement" when I do have a return statement. Does anyone know what could be causing this?
import React, { Component, useState } from "react";
import { Button, Alert, Input, Modal, ModalHeader, ModalBody, ModalFooter } from "reactstrap";
import ViewEmail from "./viewEmail";
class SingleEmail extends Component {
render() {
const ModalExample = (props) => {
const { buttonLabel, className } = props;
const [modal, setModal] = useState(false);
const toggle = () => setModal(!modal);
return (
<>
<div>
<Modal isOpen={modal} toggle={toggle} className={className}>
<ModalHeader toggle={toggle}>Modal title</ModalHeader>
<ModalBody>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
in reprehenderit in voluptate velit esse cillum dolore eu fugiat
nulla pariatur. Excepteur sint occaecat cupidatat non proident,
sunt in culpa qui officia deserunt mollit anim id est laborum.
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={toggle}>
Do Something
</Button>{" "}
<Button color="secondary" onClick={toggle}>
Cancel
</Button>
</ModalFooter>
</Modal>
<Alert
onClick={toggle}
className="SingleEmail"
style={{
backgroundColor: "white",
border: "1px solid lightgray",
color: "black",
}}
>
<div className="CheckBox">
<Input addon type="checkbox" />
</div>
<div className="MarkImportant">
<i class="fas fa-star"></i>
</div>
<p className="EmailFrom">{this.props.From}</p>
<p className="EmailTitle">{this.props.Subject}</p>
<p className="EmailDate">{this.props.Date}</p>
</Alert>
</div>
</>
);
};
}
}
export default SingleEmail;
You do not have a return statement inside the SingleEmail component but inside the ModalExample component which you have defined inside the render method of SingleEmail.
If you wish to use the ModelExample layout as singleEmail component, you can simply export the same component like
const SingleEmail = (props) => {
const { buttonLabel, className } = props;
const [modal, setModal] = useState(false);
const toggle = () => setModal(!modal);
return (
<>
<div>
<Modal isOpen={modal} toggle={toggle} className={className}>
<ModalHeader toggle={toggle}>Modal title</ModalHeader>
<ModalBody>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
in reprehenderit in voluptate velit esse cillum dolore eu fugiat
nulla pariatur. Excepteur sint occaecat cupidatat non proident,
sunt in culpa qui officia deserunt mollit anim id est laborum.
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={toggle}>
Do Something
</Button>{" "}
<Button color="secondary" onClick={toggle}>
Cancel
</Button>
</ModalFooter>
</Modal>
<Alert
onClick={toggle}
className="SingleEmail"
style={{
backgroundColor: "white",
border: "1px solid lightgray",
color: "black",
}}
>
<div className="CheckBox">
<Input addon type="checkbox" />
</div>
<div className="MarkImportant">
<i class="fas fa-star"></i>
</div>
<p className="EmailFrom">{props.From}</p>
<p className="EmailTitle">{props.Subject}</p>
<p className="EmailDate">{props.Date}</p>
</Alert>
</div>
</>
);
};
export default SingleEmail;
you have no return in render function, you can return ModalExample and things will be fine;
like this:
class SingleEmail extends Component {
render() {
const ModalExample = (props) => {
const { buttonLabel, className } = props;
const [modal, setModal] = useState(false);
const toggle = () => setModal(!modal);
return (
<>
<div>
....
....
....
</div>
</>
);
};
return ModalExample;
}
}

Resources